Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c11caf4
AbstractLogService: fix typo in javadoc
ctrueden Dec 14, 2016
7dfdff4
AbstractLogService: fix incorrect comment
ctrueden Dec 14, 2016
612dd3f
AbstractLogService: refactor duplicate code
ctrueden Dec 13, 2016
527c734
LogService: Add LogLevel class to organize logging levels
ctrueden Dec 13, 2016
adb7593
AbstractLogService: migrate log level extraction to LogLevel class
ctrueden Dec 13, 2016
8432e44
LogService: stop using deprecated log level constants
ctrueden Dec 13, 2016
41ec506
LogService: extract logging methods to a superinterface
ctrueden Dec 13, 2016
67fd752
Logger: funnel all logging through a single method
ctrueden Dec 14, 2016
ed464b0
AbstractLogSercive: extract class LogLevelStrategy
maarzt May 15, 2017
9506746
LogService: tweak getCallingClass method.
maarzt Feb 28, 2017
ad278b3
LogServive: add class LogMessage for representing log messages
maarzt May 15, 2017
ba419b2
StderrLogService: add LogFormatter for formatting log messages
maarzt May 15, 2017
2b7eadd
LogService: Add callback functionality to LogService
maarzt May 15, 2017
3124d5b
AbstractLogService: extract class DefaultListenableLogger
maarzt May 16, 2017
24bc6d2
Logger: Add factory methods subLogger and it's implementations.
maarzt May 16, 2017
ce1e3c8
LogService: add class LogSource to represent the source of a log message
maarzt May 16, 2017
0b39635
Logger: add factory methods for ListenableLogger to Logger interface
maarzt May 16, 2017
5fca862
LogLevelStrategy; refactor and add test
maarzt May 17, 2017
33c53be
LogSrevice: allow setting log levels for specified LogSources
maarzt May 17, 2017
ed23e6a
LogService: Test if StderrLogService writes the log to output streams
maarzt May 17, 2017
d2c3a6e
LogService: add ProprocessorPlugin LoggerPreprocessor
maarzt May 17, 2017
5511671
LoggerProcessor: Enable naming a Logger by Parameter annotation.
maarzt May 24, 2017
148c305
LogService: provide factory methods for private Loggers
maarzt May 17, 2017
133fe87
MultiOutputStream: use CopyOnWriteArrayList to simplify code
maarzt May 18, 2017
34fff09
DefaultConsoleService: use CopyOnWriteArrayList to simplify code
maarzt May 18, 2017
820a2a5
ConsoleService: replace MultiplePrintStream
maarzt May 18, 2017
808ee21
ConsoleService: let OutputListeners know if output is marked as log m…
maarzt May 19, 2017
fd525b3
LogService: Add class LogRecorder to record LogMessages and PrintStreams
maarzt May 22, 2017
181e18d
DefaultLogFormatter: enable setting visible fields & print attachments
maarzt Jun 21, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/main/java/org/scijava/console/ConsoleService.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

package org.scijava.console;

import java.io.PrintStream;
import java.util.LinkedList;

import org.scijava.plugin.HandlerService;
Expand Down
100 changes: 56 additions & 44 deletions src/main/java/org/scijava/console/DefaultConsoleService.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,12 @@
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.BiFunction;

import org.scijava.Context;
import org.scijava.console.OutputEvent.Source;
import org.scijava.log.LogLevel;
import org.scijava.log.LogService;
import org.scijava.plugin.AbstractHandlerService;
import org.scijava.plugin.Parameter;
Expand All @@ -64,16 +67,20 @@ public class DefaultConsoleService extends
@Parameter
private LogService log;

private MultiPrintStream sysout, syserr;
private OutputStreamReporter out, err;

/** List of listeners for {@code stdout} and {@code stderr} output. */
private ArrayList<OutputListener> listeners;

private OutputListener[] cachedListeners;
private List<OutputListener> listeners;

// -- ConsoleService methods --

@Override
public void initialize() {
PrintStream logErr = logStream(Source.STDERR);
PrintStream logOut = logStream(Source.STDOUT);
log.setPrintStreams(level -> (level <= LogLevel.WARN) ? logErr : logOut);
}

@Override
public void processArgs(final String... args) {
log.debug("Received command line arguments:");
Expand Down Expand Up @@ -115,35 +122,49 @@ public void processArgs(final String... args) {
@Override
public void addOutputListener(final OutputListener l) {
if (listeners == null) initListeners();
synchronized (listeners) {
listeners.add(l);
cacheListeners();
}
listeners.add(l);
}

@Override
public void removeOutputListener(final OutputListener l) {
if (listeners == null) initListeners();
synchronized (listeners) {
listeners.remove(l);
cacheListeners();
}
listeners.remove(l);
}

@Override
public void notifyListeners(final OutputEvent event) {
if (listeners == null) initListeners();
final OutputListener[] toNotify = cachedListeners;
for (final OutputListener l : toNotify)
for (final OutputListener l : listeners)
l.outputOccurred(event);
}

PrintStream logStream(Source source) {
OutputStream a = getBypassingStream(source);
OutputStream b = new OutputStreamReporter((relevance, text) -> {
final Context context = getContext();
final boolean contextual = true;
final boolean containsLog = true;
return new OutputEvent(context, source, text, contextual, containsLog);
});
return new PrintStream(new MultiOutputStream(a, b));
}

private OutputStream getBypassingStream(Source source) {
switch (source) {
case STDOUT:
return ListenableSystemStreams.out().bypass();
case STDERR:
return ListenableSystemStreams.err().bypass();
}
throw new AssertionError();
}

// -- Disposable methods --

@Override
public void dispose() {
if (out != null) sysout.getParent().removeOutputStream(out);
if (err != null) syserr.getParent().removeOutputStream(err);
if(out != null) ListenableSystemStreams.out().removeOutputStream(out);
if(err != null) ListenableSystemStreams.err().removeOutputStream(err);
}

// -- Helper methods - lazy initialization --
Expand All @@ -152,30 +173,24 @@ public void dispose() {
private synchronized void initListeners() {
if (listeners != null) return; // already initialized

sysout = multiPrintStream(System.out);
if (System.out != sysout) System.setOut(sysout);
out = new OutputStreamReporter(Source.STDOUT);
sysout.getParent().addOutputStream(out);

syserr = multiPrintStream(System.err);
if (System.err != syserr) System.setErr(syserr);
err = new OutputStreamReporter(Source.STDERR);
syserr.getParent().addOutputStream(err);
out = setupDefaultReporter(Source.STDOUT);
err = setupDefaultReporter(Source.STDERR);
ListenableSystemStreams.out().addOutputStream(out);
ListenableSystemStreams.err().addOutputStream(err);

listeners = new ArrayList<>();
cachedListeners = listeners.toArray(new OutputListener[0]);
listeners = new CopyOnWriteArrayList<>();
}

// -- Helper methods --

private void cacheListeners() {
cachedListeners = listeners.toArray(new OutputListener[listeners.size()]);
private OutputStreamReporter setupDefaultReporter(Source source) {
return new OutputStreamReporter((relevance, output) -> {
final Context context = getContext();
final boolean contextual = relevance == ThreadContext.SAME;
final boolean containsLog = false;
return new OutputEvent(context, source, output, contextual, containsLog);
});
}

private MultiPrintStream multiPrintStream(final PrintStream ps) {
if (ps instanceof MultiPrintStream) return (MultiPrintStream) ps;
return new MultiPrintStream(ps);
}
// -- Helper methods --

/**
* Gets whether two lists have exactly the same elements in them.
Expand All @@ -202,11 +217,12 @@ private boolean sameElements(final List<String> l1,
*/
private class OutputStreamReporter extends OutputStream {

/** Source of the output stream; i.e., {@code stdout} or {@code stderr}. */
private final Source source;
private final BiFunction<ThreadContext, String, OutputEvent> eventFactory;

public OutputStreamReporter(final Source source) {
this.source = source;
public OutputStreamReporter(
BiFunction<ThreadContext, String, OutputEvent> eventFactory)
{
this.eventFactory = eventFactory;
}

// -- OutputStream methods --
Expand All @@ -232,12 +248,8 @@ private ThreadContext getRelevance() {
}

private void publish(final ThreadContext relevance, final String output) {
final Context context = getContext();
final boolean contextual = relevance == ThreadContext.SAME;
final OutputEvent event =
new OutputEvent(context, source, output, contextual);
final OutputEvent event = eventFactory.apply(relevance, output);
notifyListeners(event);
}
}

}
94 changes: 94 additions & 0 deletions src/main/java/org/scijava/console/ListenableSystemStreams.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* #%L
* SciJava Common shared library for SciJava software.
* %%
* Copyright (C) 2009 - 2017 Board of Regents of the University of
* Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
* Institute of Molecular Cell Biology and Genetics.
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/

package org.scijava.console;

import java.io.OutputStream;
import java.io.PrintStream;
import java.util.function.Consumer;

/**
* ListenableSystemStream allows listing to System.out and System.err.
*
* @author Matthais Arzt
*/
class ListenableSystemStreams {

private ListenableSystemStreams() {
// prevent from being initialized
}

public static ListenableStream out() {
return LazyHolder.OUT;
}

public static ListenableStream err() {
return LazyHolder.ERR;
}

private static class LazyHolder { // using idiom for lazy-loaded singleton

private static final ListenableStream OUT = new ListenableStream(System.out,
System::setOut);
private static final ListenableStream ERR = new ListenableStream(System.err,
System::setErr);
}

public static class ListenableStream {

private final PrintStream out;
private PrintStream in;
private final MultiOutputStream multi;

ListenableStream(PrintStream out, Consumer<PrintStream> streamSetter) {
this.out = out;
this.multi = new MultiOutputStream(out);
this.in = new PrintStream(multi);
streamSetter.accept(in);
}

public void addOutputStream(OutputStream os) {
multi.addOutputStream(os);
}

public void removeOutputStream(OutputStream os) {
multi.removeOutputStream(os);
}

public PrintStream bypass() {
return out;
}

public PrintStream stream() {
return in;
}
}
}
40 changes: 10 additions & 30 deletions src/main/java/org/scijava/console/MultiOutputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
* A {@code MultiOutputStream} is a collection of constituent
Expand All @@ -48,81 +50,59 @@
*/
public class MultiOutputStream extends OutputStream {

private final ArrayList<OutputStream> streams;

private OutputStream[] cachedStreams;
private final List<OutputStream> streams;

/**
* Forwards output to a list of output streams.
*
* @param os Output streams which will receive this stream's output.
*/
public MultiOutputStream(final OutputStream... os) {
streams = new ArrayList<>(os.length);
for (int i = 0; i < os.length; i++) {
streams.add(os[i]);
}
cacheStreams();
streams = new CopyOnWriteArrayList<>(os);
}

// -- MultiOutputStream methods --

/** Adds an output stream to those receiving this stream's output. */
public void addOutputStream(final OutputStream os) {
synchronized (streams) {
streams.add(os);
cacheStreams();
}
streams.add(os);
}

/** Removes an output stream from those receiving this stream's output. */
public void removeOutputStream(final OutputStream os) {
synchronized (streams) {
streams.remove(os);
cacheStreams();
}
streams.remove(os);
}

// -- OutputStream methods --

@Override
public void write(final int b) throws IOException {
final OutputStream[] toWrite = cachedStreams;
for (final OutputStream stream : toWrite)
for (final OutputStream stream : streams)
stream.write(b);
}

@Override
public void write(final byte[] buf, final int off, final int len)
throws IOException
{
final OutputStream[] toWrite = cachedStreams;
for (final OutputStream stream : toWrite)
for (final OutputStream stream : streams)
stream.write(buf, off, len);
}

// -- Closeable methods --

@Override
public void close() throws IOException {
final OutputStream[] toClose = cachedStreams;
for (final OutputStream stream : toClose)
for (final OutputStream stream : streams)
stream.close();
}

// -- Flushable methods --

@Override
public void flush() throws IOException {
final OutputStream[] toFlush = cachedStreams;
for (final OutputStream stream : toFlush)
for (final OutputStream stream : streams)
stream.flush();
}

// -- Helper methods --

private void cacheStreams() {
cachedStreams = streams.toArray(new OutputStream[streams.size()]);
}

}
Loading