/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.logging.internal;
import org.gradle.api.UncheckedIOException;
import org.gradle.api.logging.LogLevel;
import org.gradle.api.logging.StandardOutputListener;
import org.gradle.listener.ListenerBroadcast;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintStream;
/**
* A {@link org.gradle.logging.internal.OutputEventListener} implementation which renders output events to various
* destinations. This implementation is thread-safe.
*/
public class OutputEventRenderer implements OutputEventListener, LoggingConfigurer, LoggingOutputInternal {
private final ListenerBroadcast<OutputEventListener> formatters = new ListenerBroadcast<OutputEventListener>(OutputEventListener.class);
private final ListenerBroadcast<StandardOutputListener> stdoutListeners = new ListenerBroadcast<StandardOutputListener>(StandardOutputListener.class);
private final ListenerBroadcast<StandardOutputListener> stderrListeners = new ListenerBroadcast<StandardOutputListener>(StandardOutputListener.class);
private final Object lock = new Object();
private final DefaultColorMap colourMap = new DefaultColorMap();
private LogLevel logLevel = LogLevel.LIFECYCLE;
public OutputEventRenderer() {
OutputEventListener stdOutChain = onNonError(new ProgressLogEventGenerator(new StyledTextOutputBackedRenderer(new StreamingStyledTextOutput(stdoutListeners.getSource())), false));
formatters.add(stdOutChain);
OutputEventListener stdErrChain = onError(new ProgressLogEventGenerator(new StyledTextOutputBackedRenderer(new StreamingStyledTextOutput(stderrListeners.getSource())), false));
formatters.add(stdErrChain);
}
public void colorStdOutAndStdErr(boolean colorOutput) {
synchronized (lock) {
colourMap.setUseColor(colorOutput);
}
}
public OutputEventRenderer addStandardOutputAndError() {
TerminalDetector terminalDetector = new TerminalDetector();
boolean stdOutIsTerminal = terminalDetector.isSatisfiedBy(FileDescriptor.out);
boolean stdErrIsTerminal = terminalDetector.isSatisfiedBy(FileDescriptor.err);
if (stdOutIsTerminal) {
PrintStream outStr = org.fusesource.jansi.AnsiConsole.out();
Console console = new AnsiConsole(outStr, outStr, colourMap);
addConsole(console, true, stdErrIsTerminal);
} else if (stdErrIsTerminal) {
// Only stderr is connected to a terminal
PrintStream errStr = org.fusesource.jansi.AnsiConsole.err();
Console console = new AnsiConsole(errStr, errStr, colourMap);
addConsole(console, false, true);
}
if (!stdOutIsTerminal) {
addStandardOutput(System.out);
}
if (!stdErrIsTerminal) {
addStandardError(System.err);
}
return this;
}
public OutputEventRenderer addStandardOutput(final Appendable out) {
addStandardOutputListener(new StandardOutputListener() {
public void onOutput(CharSequence output) {
try {
out.append(output);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
});
return this;
}
public OutputEventRenderer addStandardError(final Appendable err) {
addStandardErrorListener(new StandardOutputListener() {
public void onOutput(CharSequence output) {
try {
err.append(output);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
});
return this;
}
public void addOutputEventListener(OutputEventListener listener) {
formatters.add(listener);
}
public void removeOutputEventListener(OutputEventListener listener) {
formatters.remove(listener);
}
public OutputEventRenderer addConsole(final Console console, boolean stdout, boolean stderr) {
final OutputEventListener consoleChain = new ConsoleBackedProgressRenderer(new ProgressLogEventGenerator(new StyledTextOutputBackedRenderer(console.getMainArea()), true), console);
synchronized (lock) {
if (stdout && stderr) {
formatters.add(consoleChain);
} else if (stdout) {
formatters.add(onNonError(consoleChain));
} else {
formatters.add(onError(consoleChain));
}
}
return this;
}
private OutputEventListener onError(final OutputEventListener listener) {
return new OutputEventListener() {
public void onOutput(OutputEvent event) {
if (event.getLogLevel() == LogLevel.ERROR || event.getLogLevel() == null) {
listener.onOutput(event);
}
}
};
}
private OutputEventListener onNonError(final OutputEventListener listener) {
return new OutputEventListener() {
public void onOutput(OutputEvent event) {
if (event.getLogLevel() != LogLevel.ERROR || event.getLogLevel() == null) {
listener.onOutput(event);
}
}
};
}
public void addStandardErrorListener(StandardOutputListener listener) {
synchronized (lock) {
stderrListeners.add(listener);
}
}
public void addStandardOutputListener(StandardOutputListener listener) {
synchronized (lock) {
stdoutListeners.add(listener);
}
}
public void removeStandardOutputListener(StandardOutputListener listener) {
synchronized (lock) {
stdoutListeners.remove(listener);
}
}
public void removeStandardErrorListener(StandardOutputListener listener) {
synchronized (lock) {
stderrListeners.remove(listener);
}
}
public void configure(LogLevel logLevel) {
onOutput(new LogLevelChangeEvent(logLevel));
}
public void onOutput(OutputEvent event) {
synchronized (lock) {
if (event.getLogLevel() != null && event.getLogLevel().compareTo(logLevel) < 0) {
return;
}
if (event instanceof LogLevelChangeEvent) {
LogLevelChangeEvent changeEvent = (LogLevelChangeEvent) event;
LogLevel newLogLevel = changeEvent.getNewLogLevel();
if (newLogLevel == this.logLevel) {
return;
}
this.logLevel = newLogLevel;
}
formatters.getSource().onOutput(event);
}
}
}