【问题标题】:Redirecting SLF4J log to TextArea in JavaFX将 SLF4J 日志重定向到 JavaFX 中的 TextArea
【发布时间】:2017-01-07 22:28:26
【问题描述】:

我想在 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

我该如何解决这个问题?

【问题讨论】:

    标签: java javafx stream textarea slf4j


    【解决方案1】:

    这个答案取决于你的底层日志框架是 logback。 由于 SLF4J 一次只支持一个日志记录实现,我认为不可能以与实现无关的方式解决。

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

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

    基本 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 配置

    <?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>
    

    【讨论】:

    • 我编辑了一些代码使其可编译,但除此之外它很棒。谢谢!
    • 不支持非ascii字符。
    【解决方案2】:

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

    在您的应用程序中配置这些流(setOutsetErr)以写入文本区域。

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

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

    也看看下面的回答:https://stackoverflow.com/a/9219837/506855

    【讨论】:

      猜你喜欢
      • 2012-01-29
      • 2012-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-16
      • 2020-04-23
      • 2017-03-08
      相关资源
      最近更新 更多