Skip to content

Commit 9b908a7

Browse files
maarztctrueden
authored andcommitted
Allow subscribing to log events via LogListeners
Signed-off-by: Curtis Rueden <ctrueden@wisc.edu>
1 parent 05c6957 commit 9b908a7

File tree

6 files changed

+166
-3
lines changed

6 files changed

+166
-3
lines changed

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@
3333
package org.scijava.log;
3434

3535
import java.util.HashMap;
36+
import java.util.List;
3637
import java.util.Map;
3738
import java.util.Properties;
39+
import java.util.concurrent.CopyOnWriteArrayList;
3840

3941
import org.scijava.service.AbstractService;
4042

@@ -53,6 +55,8 @@ public abstract class AbstractLogService extends AbstractService implements
5355

5456
private final Map<String, Integer> classAndPackageLevels;
5557

58+
private final List<LogListener> listeners = new CopyOnWriteArrayList<>();
59+
5660
// -- constructor --
5761

5862
public AbstractLogService() {
@@ -68,6 +72,13 @@ public AbstractLogService(final Properties properties) {
6872
LogService.LOG_LEVEL_PROPERTY + ":");
6973
}
7074

75+
// -- AbstractLogService methods --
76+
77+
protected void notifyListeners(LogMessage message) {
78+
for (LogListener listener : listeners)
79+
listener.messageLogged(message);
80+
}
81+
7182
// -- Logger methods --
7283

7384
@Override
@@ -87,6 +98,21 @@ public void setLevel(final String classOrPackageName, final int level) {
8798
classAndPackageLevels.put(classOrPackageName, level);
8899
}
89100

101+
@Override
102+
public void alwaysLog(final int level, final Object msg, final Throwable t) {
103+
notifyListeners(new LogMessage(level, msg, t));
104+
}
105+
106+
@Override
107+
public void addListener(final LogListener listener) {
108+
listeners.add(listener);
109+
}
110+
111+
@Override
112+
public void removeListener(final LogListener listener) {
113+
listeners.remove(listener);
114+
}
115+
90116
// -- Deprecated --
91117

92118
/** @deprecated Use {@link LogLevel#prefix(int)} instead. */
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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+
* Callback function used by {@link Logger}.
36+
*
37+
* @author Matthias Arzt
38+
* @see LogMessage
39+
*/
40+
public interface LogListener {
41+
42+
/**
43+
* This method is normally called from many threads in parallel. It must be
44+
* implemented highly thread safe and must not use any kind of locks.
45+
*/
46+
void messageLogged(LogMessage message);
47+
48+
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,15 @@ default void log(final int level, final Object msg, final Throwable t) {
184184

185185
/** Returns the log level of this logger. see {@link LogLevel} */
186186
int getLevel();
187+
188+
/**
189+
* {@link LogListener}s added with this method are notified of every message,
190+
* NB: Messages are only logged, if their level is lower than the logger's
191+
* level.
192+
*
193+
* @param listener
194+
*/
195+
void addListener(LogListener listener);
196+
197+
void removeListener(LogListener listener);
187198
}

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,10 @@
5353
public class StderrLogService extends AbstractLogService {
5454

5555
@Override
56-
public void alwaysLog(final int level, final Object msg, final Throwable t) {
57-
final PrintStream out = (level <= LogLevel.WARN) ? System.err : System.out;
58-
out.print(new LogMessage(level, msg, t));
56+
public void notifyListeners(final LogMessage message) {
57+
super.notifyListeners(message);
58+
final PrintStream out = (message.level() <= LogLevel.WARN) ? System.err
59+
: System.out;
60+
out.print(message);
5961
}
6062
}

src/test/java/org/scijava/log/LogServiceTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,19 @@ public void testPackageLogLevel() {
165165
assertEquals(LogLevel.TRACE, level);
166166
}
167167

168+
@Test
169+
public void testListener() {
170+
// setup
171+
TestableLogService logService = new TestableLogService();
172+
TestLogListener listener = new TestLogListener();
173+
String msg1 = "Hello World!";
174+
// process
175+
logService.addListener(listener);
176+
logService.error(msg1);
177+
// test
178+
listener.hasLogged(m -> msg1.equals(m.text()));
179+
}
180+
168181
// -- Helper classes --
169182

170183
private static class MyTestClass {
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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.util.ArrayList;
35+
import java.util.List;
36+
import java.util.function.Predicate;
37+
38+
/**
39+
* TestLogListener is a {@ling LogListener} usable for testing. It stores all
40+
* the LogMessages it receives and allows to test if any LogMessage fulfills a
41+
* given predicate.
42+
*
43+
* @author Matthias Arzt
44+
*/
45+
46+
public class TestLogListener implements LogListener {
47+
48+
List<LogMessage> messages = new ArrayList<>();
49+
50+
@Override
51+
public void messageLogged(LogMessage message) {
52+
messages.add(message);
53+
}
54+
55+
public boolean hasLogged(Predicate<LogMessage> predicate) {
56+
return messages.stream().anyMatch(predicate);
57+
}
58+
59+
public void clear() {
60+
messages.clear();
61+
}
62+
63+
}

0 commit comments

Comments
 (0)