将 SLF4J 日志重定向到 JavaFX 中的 TextArea
Redirecting SLF4J log to TextArea in JavaFX
我想在 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));
}
}
}
和一个 class 来处理追加:
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
在控制器 class 中,我不知道如何将它们 link 放在一起。特别是当 SLF4J 自己创建 AppTA
实例时——我真的没有办法将我的 TextArea
传递给记录器使用的 AppTA
。
我该如何解决这个问题?
此答案取决于您的底层日志记录框架是 logback。
由于 SLF4J 一次仅支持一个日志记录实现,因此我认为它不可能以与实现无关的方式解决。
解决此问题的最简单方法是创建您自己的附加程序并利用静态状态,以便您可以跨应用程序访问流。
下面我演示了一个附加程序的基本示例,它可以随时静态设置其输出流。
这有一些限制,主要是它一次只能处理一个输出流,但扩展到支持多个应该不难。
在下面的应用程序中,当您单击日志按钮时,它将记录信息和错误消息,所有输出都将转到标准输出,但文本区域中只会显示错误消息。
基本 JavaFx 应用程序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));
}
}
}
简单的自定义附加器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 配置
<?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>
您可以将 Logback 配置为写入 System.out and System.err。
在您的应用程序中配置这些流 (setOut, setErr) 以写入文本区域。
此解决方案应适用于任何 SLF4J 绑定,无需更改代码。
基本上你会实现一个只读的 JavaFX 控制台。
另请参阅以下答案:
我想在 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));
}
}
}
和一个 class 来处理追加:
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
在控制器 class 中,我不知道如何将它们 link 放在一起。特别是当 SLF4J 自己创建 AppTA
实例时——我真的没有办法将我的 TextArea
传递给记录器使用的 AppTA
。
我该如何解决这个问题?
此答案取决于您的底层日志记录框架是 logback。 由于 SLF4J 一次仅支持一个日志记录实现,因此我认为它不可能以与实现无关的方式解决。
解决此问题的最简单方法是创建您自己的附加程序并利用静态状态,以便您可以跨应用程序访问流。
下面我演示了一个附加程序的基本示例,它可以随时静态设置其输出流。
这有一些限制,主要是它一次只能处理一个输出流,但扩展到支持多个应该不难。
在下面的应用程序中,当您单击日志按钮时,它将记录信息和错误消息,所有输出都将转到标准输出,但文本区域中只会显示错误消息。
基本 JavaFx 应用程序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));
}
}
}
简单的自定义附加器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 配置
<?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>
您可以将 Logback 配置为写入 System.out and System.err。
在您的应用程序中配置这些流 (setOut, setErr) 以写入文本区域。
此解决方案应适用于任何 SLF4J 绑定,无需更改代码。
基本上你会实现一个只读的 JavaFX 控制台。
另请参阅以下答案: