gpt4 book ai didi

java - 将 log4j2 输出附加到 TextArea

转载 作者:行者123 更新时间:2023-12-01 17:16:47 25 4
gpt4 key购买 nike

我正在 log4j2 中寻找一种简单的方法来将日志附加到文本区域。

在 log4j 中,这可以通过扩展 AppenderSkeleton 类来实现,但我在 log4j2 中找不到类似的机制。

此外,使用

重新路由系统输出
System.setOut(myPrintStream);

也不起作用。

有可能让它与 log4j2 一起使用吗?

最佳答案

答案很晚,但我终于找到了问题的解决方案。要将 log4j2 控制台流附加到 JavaFX TextArea、ListView 或类似的控件甚至 swing 控件,都是可能的。我的解决方案还将标准 System.out 附加到记录器 View 。看看你自己。

首先,我通过屏幕截图向您展示我当前的结果(抱歉,我无法直接将其包含在此处,因为我在 stackoverflow 上的用户帐户没有足够的声誉...): View the screenshot

第 1 步:编辑 log4j2.xml 文件并添加属性 follow="true":

<?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 Controller 以及一个 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>

Controller 类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 "))”之类的内容进行过滤真的很难看。

如果您对此有任何疑问,请告诉我。该帖子确实很旧,但如果您在这里写信,我会收到一封电子邮件。那我就可以直接回答你了

关于java - 将 log4j2 输出附加到 TextArea,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21475576/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com