我正在尋找log4j2中的一個簡單方法來將日誌追加到textArea。將log4j2輸出附加到TextArea
在log4j中,這可以通過擴展AppenderSkeleton類來實現,但我在log4j2中找不到類似的機制。
此外,
System.setOut(myPrintStream);
轉引系統輸出也不起作用。
有沒有可能讓這個與log4j2一起工作?
我正在尋找log4j2中的一個簡單方法來將日誌追加到textArea。將log4j2輸出附加到TextArea
在log4j中,這可以通過擴展AppenderSkeleton類來實現,但我在log4j2中找不到類似的機制。
此外,
System.setOut(myPrintStream);
轉引系統輸出也不起作用。
有沒有可能讓這個與log4j2一起工作?
它看起來像你正在嘗試做一些與此類似:https://issues.apache.org/jira/browse/LOG4J2-303
那吉拉票有部分解決方案。您可以查看ConsoleAppender的代碼以瞭解如何創建自定義appender。
您可以配置自定義的appender在log4j2.xml就像配置元素的內置附加目的地,但你需要在包來指定插件的包屬性(這樣log4j2插件管理能找到你的插件。)
答案很晚,但我終於找到了解決問題的辦法。 要將一個log4j2控制檯流附加到JavaFX TextArea,ListView或類似的控件,甚至是一個揮杆控制都是可能的。我的解決方案還將標準System.out附加到記錄器視圖。看看你自己。
起初,我告訴你的截圖我現在的結果(對不起,我不能在這裏直接包含它,因爲我已經在計算器...沒有足夠的信譽與我的用戶帳戶): View the screenshot
第1步:編輯您log4j2.xml文件,並添加屬性遵循=「真」:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT" follow="true">
<PatternLayout pattern="%d %-5p (%F:%L) - %m%n" />
</Console>
</Appenders>
<Loggers>
<Root level="DEBUG">
<AppenderRef ref="STDOUT" />
</Root>
</Loggers>
</Configuration>
第2步:寫一個類,它的用途是將SYSTEM_OUT追加到所需的視覺控制
對於我的例子,我編寫了一個小的UI控件組成,如截圖所示。因此需要一個FXML,對於FXML和另外一個LogStringCell的控制器,它提供了顏色格式(這是沒有必要螺母好的噱頭)
LoggerConsole.fxml
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<VBox prefHeight="200.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tuc.plentyfx.configurator.views.LoggerConsoleController">
<children>
<ListView fx:id="listViewLog" />
<ToolBar prefHeight="40.0" prefWidth="200.0">
<items>
<Button mnemonicParsing="false" onAction="#handleRemoveSelected" text="Selektierte löschen" />
<Button fx:id="buttonClearLog" mnemonicParsing="false" onAction="#handleClearLog" text="Leeren" />
<ToggleButton fx:id="toggleButtonAutoScroll" mnemonicParsing="false" text="Auto-Scroll" />
<ChoiceBox fx:id="choiceBoxLogLevel" prefWidth="150.0" />
</items>
</ToolBar>
</children>
</VBox>
控制器類LoggerConsoleController.java
package tuc.plentyfx.configurator.views;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintStream;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.ToggleButton;
import javafx.util.Callback;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import tuc.plentyfx.common.LogStringCell;
public class LoggerConsoleController {
static final Logger logger = LogManager.getLogger(LoggerConsoleController.class.getName());
@FXML
private ListView<String> listViewLog;
@FXML
private ToggleButton toggleButtonAutoScroll;
@FXML
private ChoiceBox<Level> choiceBoxLogLevel;
@FXML
void handleRemoveSelected() {
listViewLog.getItems().removeAll(listViewLog.getSelectionModel().getSelectedItems());
}
@FXML
void handleClearLog() {
listViewLog.getItems().clear();
}
@FXML
void initialize() {
listViewLog.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
Configuration loggerConfiguration = loggerContext.getConfiguration();
LoggerConfig loggerConfig = loggerConfiguration.getLoggerConfig(LogManager.ROOT_LOGGER_NAME);
/* ChoiceBox füllen */
for (Level level : Level.values()) {
choiceBoxLogLevel.getItems().add(level);
}
/* Aktuellen LogLevel in der ChoiceBox als Auswahl setzen */
choiceBoxLogLevel.getSelectionModel().select(loggerConfig.getLevel());
choiceBoxLogLevel.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Level>() {
@Override
public void changed(ObservableValue<? extends Level> arg0, Level oldLevel, Level newLevel) {
loggerConfig.setLevel(newLevel);
loggerContext.updateLoggers(); // übernehme aktuellen LogLevel
}
});
listViewLog.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
@Override
public ListCell<String> call(ListView<String> listView) {
return new LogStringCell();
}
});
/* den Origial System.out Stream in die ListView umleiten */
PipedOutputStream pOut = new PipedOutputStream();
System.setOut(new PrintStream(pOut));
PipedInputStream pIn = null;
try {
pIn = new PipedInputStream(pOut);
}
catch (IOException e) {
e.printStackTrace();
}
BufferedReader reader = new BufferedReader(new InputStreamReader(pIn));
Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
while (!isCancelled()) {
try {
String line = reader.readLine();
if (line != null) {
Platform.runLater(new Runnable() {
@Override
public void run() {
listViewLog.getItems().add(line);
/* Auto-Scroll + Select */
if (toggleButtonAutoScroll.selectedProperty().get()) {
listViewLog.scrollTo(listViewLog.getItems().size() - 1);
listViewLog.getSelectionModel().select(listViewLog.getItems().size() - 1);
}
}
});
}
}
catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
};
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
}
的LogStringCell.class
package tuc.plentyfx.common;
import javafx.scene.control.ListCell;
import javafx.scene.layout.FlowPane;
import javafx.scene.text.Text;
public class LogStringCell extends ListCell<String> {
@Override
protected void updateItem(String string, boolean empty) {
super.updateItem(string, empty);
if (string != null && !isEmpty()) {
setGraphic(createAssembledFlowPane(string));
}
else {
setGraphic(null);
setText(null);
}
}
/* Erzeuge ein FlowPane mit gefüllten Textbausteien */
private FlowPane createAssembledFlowPane(String... messageTokens) {
FlowPane flow = new FlowPane();
for (String token : messageTokens) {
Text text = new Text(token);
if (text.toString().contains(" TRACE ")) {
text.setStyle("-fx-fill: #0000FF");
}
if (text.toString().contains(" ALL ")) {
text.setStyle("-fx-fill: #FF00FF");
}
if (text.toString().contains(" ERROR ")) {
text.setStyle("-fx-fill: #FF8080");
}
if (text.toString().contains(" INFO ")) {
text.setStyle("-fx-fill: #000000");
}
if (text.toString().contains(" FATAL ")) {
text.setStyle("-fx-fill: #FF0000");
}
if (text.toString().contains(" DEBUG ")) {
text.setStyle("-fx-fill: #808080");
}
if (text.toString().contains(" OFF ")) {
text.setStyle("-fx-fill: #8040FF");
}
if (text.toString().contains(" WARN ")) {
text.setStyle("-fx-fill: #FF8000");
}
flow.getChildren().add(text);
}
return flow;
}
}
第3步:用它在你的應用程序。完成...!
問題/創意/限制: 與線程使用它時,目前有一些問題,我的代碼:創建從另一個線程日誌聲明將打破管道流並拋出一個錯誤。也許需要一個同步管道。我發現這些代碼與谷歌,但沒有嘗試它,但(看起來也是在這裏:http://www.certpal.com/blogs/2010/11/using-a-pipedinputstream-and-pipedoutputstream/ ):
package application.common;
import java.io.InputStream;
import java.io.OutputStream;
public class SyncPipe implements Runnable {
private final OutputStream outputStream;
private final InputStream inputStream;
public SyncPipe(InputStream inputStream, OutputStream outputStream) {
this.inputStream = inputStream;
this.outputStream = outputStream;
}
@Override
public void run() {
try {
final byte[] buffer = new byte[1024];
for (int length = 0; (length = inputStream.read(buffer)) != -1;) {
outputStream.write(buffer, 0, length);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
另一個問題:彩色串格式應與日誌消息類型的一些更好的檢測(信息進行重新編碼,調試,跟蹤等)。過濾諸如「text.toString()。contains(」TRACE「))」真的很醜。
讓我知道如果您有任何問題。這個線程真的很老,但如果你寫在這裏,我會收到一封電子郵件。然後我可以直接回復你
[AbstractAppender](http://logging.apache.org/log4j/2.0/log4j-core/apidocs/org/apache/logging/log4j/core/appender/AbstractAppender.html )看起來相當於[AppenderSkeleton](http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/AppenderSkeleton.html) – vanOekel