前言
之前在自己开发一个软件的时候想着实现一个打印日志到控制台的功能,网上找了一大堆资料实在晦涩,不是看不懂就是运行一堆报错,截止前两分钟刚用第二种方式实现了这个功能,这也是咱 "独立实现" 解决问题的第一次,之前都是参考别人的代码。本着互惠互利、博爱共享的开源精神,我必须记录一下。
两种方式
这里的例子都用 JavaFX 的 TextArea 来实现,你要换用别的当然可以,Button、Label 都行,你开心就好。
主要有两种方式:
- 使用 org.apache.log4j.ConsoleAppender ,这是 log4j 自带的一个输出源,它会把日志输出到控制台,我们只需要把日志从控制台搜集起来写到 TextArea 就 ok 了。
- 自定义一个 Appender,需要实现 log4j 的 AppenderSkeleton 接口,实现三个方法。
1、ConsoleAppender 重定向
简简单单先写一个线程类:
package com.lyh.utils; import java.io.IOException; import java.io.PipedReader; import java.io.PipedWriter; import java.io.Writer; import org.apache.log4j.Logger; import org.apache.log4j.Appender; import org.apache.log4j.WriterAppender; public abstract class LogAppender extends Thread{ protected PipedReader reader; public LogAppender(String appenderName) throws IOException { Logger root = Logger.getRootLogger(); // 获取子记录器的输出源 Appender appender = root.getAppender(appenderName); // 定义一个未连接的输入流管道 reader = new PipedReader(); // 定义一个已连接的输出流管理,并连接到reader Writer writer = new PipedWriter(reader); // 设置 appender 输出流 ((WriterAppender) appender).setWriter(writer); } }
直接 copy 不要想为什么,然后通过 Scanner 实现把控制台的数据写到 textArea 里:
package com.lyh.utils; import javafx.application.Platform; import javafx.scene.control.TextArea; import java.io.IOException; import java.util.Scanner; public class CodeAreaLogAppender extends LogAppender { private TextArea textArea; private StringBuilder builder = new StringBuilder(); private int currentRow = 0; public CodeAreaLogAppender(TextArea textArea) throws IOException { super("textArea"); this.textArea = textArea; } @Override public void run() { Scanner scanner = new Scanner(reader); // 将扫描到的字符流显示在指定的TextArea上 while (scanner.hasNextLine()) { try { // 非 UI 操作涉及到变量的不要放到 UI 线程去,因为线程的运行不同步 String line = scanner.nextLine(); Platform.runLater(()->{ textArea.appendText(line+"\n"); }); } catch (Exception ex) { ex.printStackTrace(); } } } }
ok 大功告成!
使用的时候直接:
new CodeAreaLogAppender(textArea).start();
2、自定义 Appender
第二种方式就更简洁了,只需要实现 log4j 的 AppenderSkeleton 这个抽象类。重点是实现单例模式保证这个 textArea 和 MyLogAppender 类的实例唯一。
package com.lyh.utils; import javafx.application.Platform; import javafx.scene.control.TextArea; import org.apache.log4j.AppenderSkeleton; import org.apache.log4j.Logger; import org.apache.log4j.spi.LoggingEvent; public class MyLogAppender extends AppenderSkeleton { private final static Logger logger = Logger.getLogger(MyLogAppender.class); private static MyLogAppender appender; public static TextArea textArea = new TextArea(); public static MyLogAppender getInstance(){ if (appender == null) appender = new MyLogAppender(); return appender; } /** * 打印日志的核心方法 * @param loggingEvent */ @Override protected void append(LoggingEvent loggingEvent) { String message = loggingEvent.getMessage().toString(); Platform.runLater(()->{ textArea.setText(message); }); } /** * 释放资源 */ @Override public void close() { // 不用就不需要实现 } /** * 是否需要按照格式输出文本 * @return */ @Override public boolean requiresLayout() { return false; } }
使用的时候:
MyLogAppender.getInstance(); vBox.getChildren().add(MyLogAppender.textArea);
总结
设计模式还是很有必要了解的,能帮助我们更好的理解 OOP 思想。