在JavaFX中将SLF4J日志重定向到TextArea

时间:2016-08-31 11:00:04

标签: java javafx stream textarea slf4j

我想在JavaFX中的TextArea中显示SLF4J记录的错误。到目前为止我在 logback-test.xml中的appender

<appender name="err" class="logtest.AppTA">
    <filter class="logtest.ErrFilter" />
    <encoder>
        <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
    </encoder>
</appender>

TextArea准备接收信息流:

public class Output extends OutputStream{
    private final TextArea ta;
    public Output(TextArea ta) {
        this.ta = ta;
    }
    @Override
    public void write(int b) throws IOException {
        if (ta!=null) {
            ta.appendText(String.valueOf((char) b));
        }
    }
}

和一个处理追加的类:

public class AppTA extends AppenderBase<ILoggingEvent> {

    PatternLayoutEncoder encoder;
    OutputStream os;

    @Override
    protected void append(ILoggingEvent event) {
        try {
            if (isEncoderInitialized) {
                this.encoder.doEncode(event);
            }
        } catch (IOException e) {
        }
    }

    @Override
    public void start() {
        if (this.encoder == null) {
            addError("No encoder set for the appender named [" + name + "].");
            return;
        }
        try {
            encoder.init(os);
        } catch (IOException ex) {
            Logger.getLogger(AppTA.class.getName()).log(Level.SEVERE, null, ex);
        }
        super.start();
    }

    public PatternLayoutEncoder getEncoder() {
        return encoder;
    }

    public void setEncoder(PatternLayoutEncoder encoder) {
        this.encoder = encoder;
    }
}

现在我遇到的问题是我的TextArea在控制器类中,我不知道如何将它们链接在一起。特别是当SLF4J自己创建AppTA个实例时 - 我真的没办法将TextArea传递给记录器使用的AppTA

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:6)

此答案取决于您的基础日志框架是否为logback。 由于SLF4J一次只支持一个日志记录实现,因此我认为不可能以与实现无关的方式解决。

解决此问题的最简单方法是创建自己的appender并利用静态状态,以便可以跨应用程序访问流。
下面我演示一个appender的基本示例,它可以随时静态设置其输出流 这有一些限制,主要是它一次只能处理一个输出流,不应该太难以扩展以支持多个输出流。

在下面的应用程序中,当您单击日志按钮时,它将记录信息和错误消息,所有输出将转到stdout,但只有错误消息将显示在文本区域中。

基本JavaFx应用程序类

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));
        }
    }
}

简单的自定义appender类

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>

答案 1 :(得分:1)

您可以将Logback配置为写入System.outSystem.err

在应用程序中配置这些流(setOutsetErr)以写入textarea。

此解决方案适用于任何SLF4J绑定,无需更改代码。

基本上你会实现一个只读的JavaFX控制台。

另请查看以下答案:https://stackoverflow.com/a/9219837/506855