I would like to show errors logged by SLF4J in TextArea
in JavaFX. What I have so far is an appender
in logback-test.xml:
<
You could configure Logback to write to System.out and System.err.
Configure those streams (setOut, setErr) in your application to write to the textarea.
This solution should work with any SLF4J binding without code changes.
Basically you would implement a read-only JavaFX console.
Also have a look at the following answer: https://stackoverflow.com/a/9219837/506855
This answer is dependent on your underlying logging framework being logback. Since SLF4J only supports one logging implementation at a time I don't think its possible to solve in an implementation agnostic way.
The simplest way to solve this issue is to create your own appender and utilize static state so you can access the streams across the application.
Below I demonstrate a basic example of an appender that can have its output stream set statically at any time.
This has some limitations, mainly that it can only handle one outputstream at a time, bu tit shouldn't be too difficult to extend to support multiple.
In the below application when you click the log button it will log info and error messages, all output will go to stdout but only error messsages will be shown in the text area.
Basic JavaFx application class
public class Main extends Application {
private static final Logger LOG = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Log stuff");
btn.setOnAction(a-> {
LOG.info("This is some info");
LOG.error("This is some error");
});
TextArea textArea = new TextArea();
OutputStream os = new TextAreaOutputStream(textArea);
MyStaticOutputStreamAppender.setStaticOutputStream(os);
GridPane grid = new GridPane();
grid.add(textArea, 0 ,0);
grid.add(btn, 0, 1);
primaryStage.setScene(new Scene(grid, 500, 250));
primaryStage.show();
}
private static class TextAreaOutputStream extends OutputStream {
private TextArea textArea;
public TextAreaOutputStream(TextArea textArea) {
this.textArea = textArea;
}
@Override
public void write(int b) throws IOException {
textArea.appendText(String.valueOf((char) b));
}
}
}
Simple custom appender class
public class MyStaticOutputStreamAppender<E> extends OutputStreamAppender<E> {
private static final DelegatingOutputStream DELEGATING_OUTPUT_STREAM = new DelegatingOutputStream(null);
@Override
public void start() {
setOutputStream(DELEGATING_OUTPUT_STREAM);
super.start();
}
public static void setStaticOutputStream(OutputStream outputStream) {
DELEGATING_OUTPUT_STREAM.setOutputStream(outputStream);
}
private static class DelegatingOutputStream extends FilterOutputStream {
/**
* Creates a delegating outputstream with a NO-OP delegate
*/
public DelegatingOutputStream(OutputStream out){
super(new OutputStream() {
@Override
public void write(int b) throws IOException {}
});
}
void setOutputStream(OutputStream outputStream) {
this.out = outputStream;
}
}
}
Logback config
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n</pattern>
</encoder>
</appender>
<appender name="MyCustomAppender" class="example.MyStaticOutputStreamAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n</pattern>
</encoder>
</appender>
<root>
<appender-ref ref="STDOUT" />
<appender-ref ref="MyCustomAppender" />
</root>
</configuration>