Last active
December 22, 2015 12:52
-
-
Save shnplr/81a0716a0ee305e5fd98 to your computer and use it in GitHub Desktop.
JavaFX - log4j appender that logs to observable string property
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.sample; | |
import javafx.application.Platform; | |
import javafx.beans.property.SimpleStringProperty; | |
import javafx.beans.property.StringProperty; | |
import javafx.beans.value.ChangeListener; | |
import org.apache.log4j.AppenderSkeleton; | |
import org.apache.log4j.Layout; | |
import org.apache.log4j.spi.LoggingEvent; | |
import java.util.Collections; | |
import java.util.HashMap; | |
import java.util.Map; | |
/** | |
* An appender which logs to the UI thread. For example: | |
* | |
*<t> | |
* ConsoleController.groovy - display logs inside a TextArea | |
* | |
* ObservableAppender appender = org.apache.log4j.Logger.getLogger("mylogger.console").getAppender("appconsole") | |
* appender.addLogListener( { msg -> view.builder.wtextArea.appendText(msg) } ) | |
* | |
* log4j.properties - define appender and logger config | |
* | |
* # create an appender for ui console | |
* log4j.appender.appconsole=com.sample.ObservableAppender | |
* log4j.appender.appconsole.layout=org.apache.log4j.PatternLayout | |
* log4j.appender.appconsole.layout.ConversionPattern=[%d] %-5p - %m%n | |
* | |
* # create a logger to send console output | |
* # e.g. LoggerFactory.getLogger("mylogger.console").info("a message") | |
* log4j.logger.mylogger.console=info,appconsole | |
* log4j.additivity.esbtoolsg.console=false | |
* | |
* # also send some apache log messages to the console... | |
* # log4j.logger.org.apache.commons.httpclient=warn,appconsole | |
* | |
</t> | |
*/ | |
public class ObservableAppender extends AppenderSkeleton { | |
private final StringProperty value = new SimpleStringProperty(); | |
// private String getValue() { return value.get() } | |
// private void setValue(String value) { value.set(value) } | |
// public final ReadOnlyStringProperty valueProperty() { return value } | |
// store listeners for removal | |
// impl: do not reference inside Appender.append method | |
private final Map<LogListener, ChangeListener> listeners = Collections.synchronizedMap(new HashMap<>()); | |
private boolean configured; | |
/** | |
* Default constructor does nothing | |
*/ | |
public ObservableAppender() { | |
} | |
public ObservableAppender(Layout layout) { | |
this.layout = layout; | |
} | |
public interface LogListener { | |
void log(String message); | |
} | |
/** | |
* | |
* @param listener | |
*/ | |
public void addLogListener(final LogListener listener) { | |
if (listener != null) { | |
ChangeListener<String> changeListener = (observable, oldValue, newValue) -> { | |
try { | |
listener.log(newValue); | |
} catch (Exception e) { | |
// prevent overflow if log produces an exception | |
e.printStackTrace(); | |
} | |
}; | |
if (Platform.isFxApplicationThread()) { | |
addLogListenerImpl(listener, changeListener); | |
} else { | |
Platform.runLater(() -> addLogListenerImpl(listener, changeListener)); | |
} | |
} | |
} | |
private void addLogListenerImpl(final LogListener listener, final ChangeListener changeListener) { | |
ChangeListener<String> oldListener = listeners.remove(listener); | |
if (oldListener != null) { | |
value.removeListener(oldListener); | |
} | |
value.addListener(changeListener); | |
listeners.put(listener, changeListener); | |
configured = true; | |
} | |
public void removeLogListener(final LogListener listener) { | |
if (listener != null) { | |
if (Platform.isFxApplicationThread()) { | |
removeLogListenerImpl(listener); | |
} else { | |
Platform.runLater(() -> removeLogListenerImpl(listener)); | |
} | |
} | |
} | |
private void removeLogListenerImpl(final LogListener listener) { | |
ChangeListener<String> oldListener = listeners.remove(listener); | |
if (oldListener != null) value.removeListener(oldListener); | |
configured = (listeners.size() != 0); | |
} | |
// org.apache.log4jAppender | |
@Override | |
public void close() { | |
synchronized (listeners) { | |
for (ChangeListener<String> listener : listeners.values()) { | |
value.removeListener(listener); | |
} | |
listeners.clear(); | |
} | |
configured = false; | |
} | |
private void updateValue(String value) { | |
if (Platform.isFxApplicationThread()) { | |
this.value.set(value); | |
} else { | |
Platform.runLater(() -> ObservableAppender.this.value.set(value)); | |
} | |
} | |
@Override | |
protected void append(LoggingEvent event) { | |
if (configured) { | |
updateValue(layout.format(event)); | |
if (layout.ignoresThrowable()) { | |
String[] lines = event.getThrowableStrRep(); | |
if (lines != null) { | |
StringBuilder sb = new StringBuilder(); | |
for (String line : lines) { | |
sb.append(line); | |
sb.append(Layout.LINE_SEP); | |
} | |
updateValue(sb.toString()); | |
} | |
} | |
} | |
} | |
@Override | |
public boolean requiresLayout() { | |
return true; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment