Skip to content

Commit f7b4e30

Browse files
committed
Merge branch 'logger-iface'
These commits introduce a Logger interface which LogService now extends. The Logger interface provides a better separation of concerns, allowing for multiple Logger objects instead of singleton per SciJava context.
2 parents 7dfcea0 + 51caae5 commit f7b4e30

File tree

8 files changed

+402
-245
lines changed

8 files changed

+402
-245
lines changed

src/main/java/org/scijava/log/AbstractLogService.java

Lines changed: 31 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -42,31 +42,16 @@
4242
* Base class for {@link LogService} implementations.
4343
*
4444
* @author Johannes Schindelin
45+
* @author Curtis Rueden
4546
*/
47+
@IgnoreAsCallingClass
4648
public abstract class AbstractLogService extends AbstractService implements
4749
LogService
4850
{
4951

5052
private int currentLevel = levelFromEnvironment();
5153

52-
private final Map<String, Integer> classAndPackageLevels =
53-
new HashMap<>();
54-
55-
// -- abstract methods --
56-
57-
/**
58-
* Displays a message.
59-
*
60-
* @param msg the message to display.
61-
*/
62-
protected abstract void log(final String msg);
63-
64-
/**
65-
* Displays an exception.
66-
*
67-
* @param t the exception to display.
68-
*/
69-
protected abstract void log(final Throwable t);
54+
private final Map<String, Integer> classAndPackageLevels;
7055

7156
// -- constructor --
7257

@@ -75,160 +60,21 @@ public AbstractLogService() {
7560
}
7661

7762
public AbstractLogService(final Properties properties) {
78-
// check SciJava log level system properties for initial logging levels
79-
80-
// global log level property
81-
final String logProp = properties.getProperty(LOG_LEVEL_PROPERTY);
82-
final int level = LogLevel.value(logProp);
83-
if (level >= 0) setLevel(level);
84-
85-
if (getLevel() == 0)
86-
setLevel(levelFromEnvironment());
87-
88-
// populate custom class- and package-specific log level properties
89-
final String logLevelPrefix = LOG_LEVEL_PROPERTY + ":";
90-
for (final Object propKey : properties.keySet()) {
91-
if (!(propKey instanceof String)) continue;
92-
final String propName = (String) propKey;
93-
if (!propName.startsWith(logLevelPrefix)) continue;
94-
final String classOrPackageName =
95-
propName.substring(logLevelPrefix.length());
96-
setLevel(classOrPackageName, LogLevel.value(properties.getProperty(propName)));
97-
}
98-
99-
}
100-
101-
// -- helper methods --
102-
103-
protected void log(final int level, final Object msg, final Throwable t) {
104-
if (level > getLevel()) return;
105-
106-
if (msg != null || t == null) {
107-
log(level, msg);
108-
}
109-
if (t != null) log(t);
110-
}
111-
112-
protected void log(final int level, final Object msg) {
113-
final String prefix = LogLevel.prefix(level);
114-
log((prefix == null ? "" : prefix + " ") + msg);
115-
}
116-
117-
// -- LogService methods --
118-
119-
@Override
120-
public void debug(final Object msg) {
121-
log(LogLevel.DEBUG, msg, null);
122-
}
123-
124-
@Override
125-
public void debug(final Throwable t) {
126-
log(LogLevel.DEBUG, null, t);
127-
}
128-
129-
@Override
130-
public void debug(final Object msg, final Throwable t) {
131-
log(LogLevel.DEBUG, msg, t);
132-
}
133-
134-
@Override
135-
public void error(final Object msg) {
136-
log(LogLevel.ERROR, msg, null);
137-
}
138-
139-
@Override
140-
public void error(final Throwable t) {
141-
log(LogLevel.ERROR, null, t);
142-
}
143-
144-
@Override
145-
public void error(final Object msg, final Throwable t) {
146-
log(LogLevel.ERROR, msg, t);
147-
}
148-
149-
@Override
150-
public void info(final Object msg) {
151-
log(LogLevel.INFO, msg, null);
63+
// provide this constructor to enable unit tests
64+
final int level = LogLevel.value(properties.getProperty(
65+
LogService.LOG_LEVEL_PROPERTY));
66+
if (level >= 0) currentLevel = level;
67+
classAndPackageLevels = setupMapFromProperties(properties,
68+
LogService.LOG_LEVEL_PROPERTY + ":");
15269
}
15370

154-
@Override
155-
public void info(final Throwable t) {
156-
log(LogLevel.INFO, null, t);
157-
}
158-
159-
@Override
160-
public void info(final Object msg, final Throwable t) {
161-
log(LogLevel.INFO, msg, t);
162-
}
163-
164-
@Override
165-
public void trace(final Object msg) {
166-
log(LogLevel.TRACE, msg, null);
167-
}
168-
169-
@Override
170-
public void trace(final Throwable t) {
171-
log(LogLevel.TRACE, null, t);
172-
}
173-
174-
@Override
175-
public void trace(final Object msg, final Throwable t) {
176-
log(LogLevel.TRACE, msg, t);
177-
}
178-
179-
@Override
180-
public void warn(final Object msg) {
181-
log(LogLevel.WARN, msg, null);
182-
}
183-
184-
@Override
185-
public void warn(final Throwable t) {
186-
log(LogLevel.WARN, null, t);
187-
}
188-
189-
@Override
190-
public void warn(final Object msg, final Throwable t) {
191-
log(LogLevel.WARN, msg, t);
192-
}
193-
194-
@Override
195-
public boolean isDebug() {
196-
return getLevel() >= LogLevel.DEBUG;
197-
}
198-
199-
@Override
200-
public boolean isError() {
201-
return getLevel() >= LogLevel.ERROR;
202-
}
203-
204-
@Override
205-
public boolean isInfo() {
206-
return getLevel() >= LogLevel.INFO;
207-
}
208-
209-
@Override
210-
public boolean isTrace() {
211-
return getLevel() >= LogLevel.TRACE;
212-
}
213-
214-
@Override
215-
public boolean isWarn() {
216-
return getLevel() >= LogLevel.WARN;
217-
}
71+
// -- Logger methods --
21872

21973
@Override
22074
public int getLevel() {
221-
if (!classAndPackageLevels.isEmpty()) {
222-
// check for a custom log level for calling class or its parent packages
223-
String classOrPackageName = callingClass();
224-
while (classOrPackageName != null) {
225-
final Integer level = classAndPackageLevels.get(classOrPackageName);
226-
if (level != null) return level;
227-
classOrPackageName = parentPackage(classOrPackageName);
228-
}
229-
}
230-
// no custom log level; return the global log level
231-
return currentLevel;
75+
if (classAndPackageLevels.isEmpty()) return currentLevel;
76+
return getLevelForClass(CallingClassUtils.getCallingClass().getName(),
77+
currentLevel);
23278
}
23379

23480
@Override
@@ -251,14 +97,14 @@ protected String getPrefix(final int level) {
25197

25298
// -- Helper methods --
25399

254-
private String callingClass() {
255-
final String thisClass = AbstractLogService.class.getName();
256-
for (final StackTraceElement element : new Exception().getStackTrace()) {
257-
final String className = element.getClassName();
258-
// NB: Skip stack trace elements from other methods of this class.
259-
if (!thisClass.equals(className)) return className;
100+
private int getLevelForClass(String classOrPackageName, int defaultLevel) {
101+
// check for a custom log level for calling class or its parent packages
102+
while (classOrPackageName != null) {
103+
final Integer level = classAndPackageLevels.get(classOrPackageName);
104+
if (level != null) return level;
105+
classOrPackageName = parentPackage(classOrPackageName);
260106
}
261-
return null;
107+
return defaultLevel;
262108
}
263109

264110
private String parentPackage(final String classOrPackageName) {
@@ -272,4 +118,15 @@ private int levelFromEnvironment() {
272118
return System.getenv("DEBUG") == null ? LogLevel.INFO : LogLevel.DEBUG;
273119
}
274120

121+
private Map<String, Integer> setupMapFromProperties(Properties properties,
122+
String prefix)
123+
{
124+
final HashMap<String, Integer> map = new HashMap<>();
125+
for (final String propName : properties.stringPropertyNames())
126+
if (propName.startsWith(prefix)) {
127+
final String key = propName.substring(prefix.length());
128+
map.put(key, LogLevel.value(properties.getProperty(propName)));
129+
}
130+
return map;
131+
}
275132
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* #%L
3+
* SciJava Common shared library for SciJava software.
4+
* %%
5+
* Copyright (C) 2009 - 2017 Board of Regents of the University of
6+
* Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
7+
* Institute of Molecular Cell Biology and Genetics.
8+
* %%
9+
* Redistribution and use in source and binary forms, with or without
10+
* modification, are permitted provided that the following conditions are met:
11+
*
12+
* 1. Redistributions of source code must retain the above copyright notice,
13+
* this list of conditions and the following disclaimer.
14+
* 2. Redistributions in binary form must reproduce the above copyright notice,
15+
* this list of conditions and the following disclaimer in the documentation
16+
* and/or other materials provided with the distribution.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
* POSSIBILITY OF SUCH DAMAGE.
29+
* #L%
30+
*/
31+
32+
package org.scijava.log;
33+
34+
/**
35+
* Utility class for getting the calling class of a method.
36+
*
37+
* @author Matthias Arzt
38+
*/
39+
40+
@IgnoreAsCallingClass
41+
public final class CallingClassUtils {
42+
43+
private CallingClassUtils() {
44+
// prevent instantiation of utility class
45+
}
46+
47+
/**
48+
* Inspects the stack trace to return the class that calls this method, but
49+
* ignores every class annotated with @IgnoreAsCallingClass.
50+
*
51+
* @throws IllegalStateException if every method on the stack, is in a class
52+
* annotated with @IgnoreAsCallingClass.
53+
*/
54+
public static Class<?> getCallingClass() {
55+
try {
56+
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
57+
for (int i = 1; i < stackTrace.length - 1; i++) {
58+
Class<?> clazz = Class.forName(stackTrace[i].getClassName());
59+
if (!clazz.isAnnotationPresent(IgnoreAsCallingClass.class))
60+
return clazz;
61+
}
62+
}
63+
catch (ClassNotFoundException ignore) {}
64+
throw new IllegalStateException();
65+
}
66+
67+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* #%L
3+
* SciJava Common shared library for SciJava software.
4+
* %%
5+
* Copyright (C) 2009 - 2017 Board of Regents of the University of
6+
* Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
7+
* Institute of Molecular Cell Biology and Genetics.
8+
* %%
9+
* Redistribution and use in source and binary forms, with or without
10+
* modification, are permitted provided that the following conditions are met:
11+
*
12+
* 1. Redistributions of source code must retain the above copyright notice,
13+
* this list of conditions and the following disclaimer.
14+
* 2. Redistributions in binary form must reproduce the above copyright notice,
15+
* this list of conditions and the following disclaimer in the documentation
16+
* and/or other materials provided with the distribution.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
* POSSIBILITY OF SUCH DAMAGE.
29+
* #L%
30+
*/
31+
32+
package org.scijava.log;
33+
34+
import java.lang.annotation.Retention;
35+
import java.lang.annotation.RetentionPolicy;
36+
37+
/**
38+
* Classes annotated with {@link IgnoreAsCallingClass} are ignored by
39+
* {@link CallingClassUtils#getCallingClass()}.
40+
*
41+
* @author Matthias Arzt
42+
*/
43+
@Retention(RetentionPolicy.RUNTIME)
44+
public @interface IgnoreAsCallingClass {}

0 commit comments

Comments
 (0)