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

Posted

技术标签:

【中文标题】将 SLF4J 日志重定向到 JavaFX 中的 TextArea【英文标题】:Redirecting SLF4J log to TextArea in JavaFX 【发布时间】: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 %logger35 - %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

我该如何解决这个问题?

【问题讨论】:

【参考方案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>%dHH:mm:ss.SSS [%thread] %-5level %logger5 - %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>%dHH:mm:ss.SSS [%thread] %-5level %logger5 - %msg%n</pattern>
        </encoder>
    </appender>

    <root>
        <appender-ref ref="STDOUT" />
        <appender-ref ref="MyCustomAppender" />
    </root>

</configuration>

【讨论】:

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

您可以将 Logback 配置为写入 System.out 和 System.err。

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

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

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

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

【讨论】:

以上是关于将 SLF4J 日志重定向到 JavaFX 中的 TextArea的主要内容,如果未能解决你的问题,请参考以下文章

将 System.out 重定向到 JavaFX 中的 TextArea

JavaFX:将控制台输出重定向到在 SceneBuilder 中创建的 TextArea

将 aws lambda 日志重定向到 cloudwatch 中的特定日志组

利用logback+slf4j日志采集整合到SBA

在 slf4j 运行时设置消息的日志级别

在slf4j中设置运行时消息的日志级别