如何使用 Log4j2 将日志输出到 JTextArea

2024-01-21

我已经尝试将日志输出到 JTextArea 好几天了,但仍然没有成功。基本上我尝试过的是按照现有的附加程序(如 consoleAppender)创建我自己的自定义附加程序,并尝试在 log4j2.xml 中配置它。我觉得我正朝着正确的方向前进,但不知何故我无法让它发挥作用。我已经在 log4j2 用户邮件列表中询问过,但似乎没有人愿意帮助我。希望我能在这里得到帮助。如果您知道如何实现它,请给我步骤甚至代码片段。

感谢您提前提供的帮助。

好吧,既然有人否决了我的问题,因为它没有表现出任何努力,那么我最好表现出一些努力。我没有展示我所做的任何事情,因为我不太确定我正在做的事情是否正确,人们可能有自己的方法。

我面临的问题是

  • 我找不到将该 JTextArea 对象传递给我的 TextAreaAppender 的方法
  • 当我尝试运行测试类时,总是收到一个错误,提示 TextArea Appender CLASS_NOT_FOUND,但我已经尝试了所有可能的方法,我可以找到在 log4j2.xml 中指定类属性

代码如下,

文本区域追加器

public class TextAreaAppender extends AbstractOutputStreamAppender<OutputStreamManager>{
    private static TextAreaManagerFactory factory = new TextAreaManagerFactory();

    public enum Target {
        TEXTAREA
    }

    protected TextAreaAppender(String name, Layout<? extends Serializable> layout, Filter filter,
            OutputStreamManager manager, boolean ignoreExceptions) {
        super(name, layout, filter, ignoreExceptions, true, manager);
        // TODO Auto-generated constructor stub
    }

    @PluginFactory
    public static TextAreaAppender createAppender(
            @PluginElement("Layout") Layout<? extends Serializable> layout,
            @PluginElement("Filters") final Filter filter,
            @PluginAttribute("target") final String t,
            @PluginAttribute("name") final String name,
            @PluginAttribute("follow") final String follow,
            @PluginAttribute("ignoreExceptions") final String ignore) {
        if (name == null) {
            LOGGER.error("No name provided for TextAreaAppender");
            return null;
        }
        if (layout == null) {
            layout = PatternLayout.createLayout(null, null, null, null, null, null);
        }
        final boolean isFollow = Boolean.parseBoolean(follow);
        final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
        final Target target = t == null ? Target.TEXTAREA : Target.valueOf(t);
        return new TextAreaAppender(name, layout, filter, getManager(isFollow, target, layout), ignoreExceptions);
    }

    private static OutputStreamManager getManager(final boolean follow, final Target target, final Layout<? extends Serializable> layout) {
        final String type = target.name();
        //should change to getOutputStream(JTextArea), 
        //but not sure how I can pass textarea object to this class
        final OutputStream os = getOutputStream(follow, target);
        return OutputStreamManager.getManager(target.name() + "." + follow, new FactoryData(os, type, layout), factory);
    }

    private static OutputStream getOutputStream(JTextArea ta){ 
        return new TextAreaOutputStream(ta); 
    }
    private static class TextAreaOutputStream extends OutputStream {
        private final JTextArea output;
        public TextAreaOutputStream(JTextArea ta){
            this.output = ta; 
        }
        @Override
        public void write(int i) throws IOException{
            output.append(String.valueOf((char) i)); 
        }
    }

    /**
     * Data to pass to factory method.
     */
    private static class FactoryData {
        private final OutputStream os;
        private final String type;
        private final Layout<? extends Serializable> layout;

        /**
         * Constructor.
         * @param os The OutputStream.
         * @param type The name of the target.
         * @param layout A Serializable layout
         */
        public FactoryData(final OutputStream os, final String type, final Layout<? extends Serializable> layout) {
            this.os = os;
            this.type = type;
            this.layout = layout;
        }
    }
    /**
     * Factory to create the Appender.
     */
    private static class TextAreaManagerFactory implements ManagerFactory<OutputStreamManager, FactoryData> {

        /**
         * Create an OutputStreamManager.
         * @param name The name of the entity to manage.
         * @param data The data required to create the entity.
         * @return The OutputStreamManager
         */
        @Override
        public OutputStreamManager createManager(final String name, final FactoryData data) {
            return new TextAreaOutputStreamManager(data.os, data.type, data.layout);// protected constructor???
        }
    }

    private static class TextAreaOutputStreamManager extends OutputStreamManager{

        public TextAreaOutputStreamManager(OutputStream os, String name,
                Layout<?> layout) {
            super(os, name, layout);
            // TODO Auto-generated constructor stub
        }
    }
}

UI测试类

public class Log4j2Example {
    class LogModel extends AbstractTableModel{

        @Override
        public int getColumnCount() {
            // TODO Auto-generated method stub
            return 1;
        }

        @Override
        public int getRowCount() {
            // TODO Auto-generated method stub
            return 0;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            // TODO Auto-generated method stub
            switch(columnIndex){
                case 0: return null;
                default: return null;
            }
        }

    }
    private final static JTextArea textarea = new JTextArea();
    private final LogModel model = new LogModel();
    private final JTable table = new JTable(model);
    static Log4j2Example INSTANCE = new Log4j2Example();
    JFrame frame = new JFrame();

    void run(){
        frame.setLayout(new BorderLayout());
        table.setBorder(new TitledBorder("Table"));
        textarea.setBorder(new TitledBorder("Text Area"));
        textarea.setPreferredSize(new Dimension(100, 150));
        textarea.setEditable(false);

        frame.add(table);
        frame.add(textarea, BorderLayout.SOUTH);

        frame.setVisible(true);
        frame.setSize(400, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    static final Logger logger = LogManager.getLogger(Log4j2Example.class.getName());
    public static void main(String[] args) {
        INSTANCE.run();
        System.out.println("test");
        logger.trace("Entering Log4j Example.");
        Hello hello = new Hello();
        if (!hello.callMe()) {
            logger.error("Ohh!Failed!");
        }
        logger.trace("Exiting Log4j Example.");


    }
}

log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="CONSOLE" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        </Console>
        <TextArea name="TextArea" class="testing.Log4j2Example.TextAreaAppender">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        </TextArea>
    </Appenders>
    <Loggers>
        <Logger name="testing.Log4j2Example" level="ALL">
          <AppenderRef ref="TextArea"/>
        </Logger>

        <Root level="ERROR">
            <AppenderRef ref="CONSOLE"/>
        </Root>
    </Loggers>
</Configuration>

以下方法对我有用,我基于log4j提出解决方案here http://www.rshingleton.com/javafx-log4j-textarea-log-appender以及有关的其他一般信息log4j2附加器here http://logging.apache.org/log4j/2.0/manual/appenders.html.

JTextAreaAppender.java

import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;

import javax.swing.*;
import java.util.ArrayList;

import static javax.swing.SwingUtilities.invokeLater;
import static org.apache.logging.log4j.core.config.Property.EMPTY_ARRAY;
import static org.apache.logging.log4j.core.layout.PatternLayout.createDefaultLayout;

@Plugin(name = "JTextAreaAppender", category = "Core", elementType = "appender", printObject = true)
public class JTextAreaAppender extends AbstractAppender
{
    private static volatile ArrayList<JTextArea> textAreas = new ArrayList<>();

    private int maxLines;

    private JTextAreaAppender(String name, Layout<?> layout, Filter filter, int maxLines, boolean ignoreExceptions)
    {
        super(name, filter, layout, ignoreExceptions, EMPTY_ARRAY);
        this.maxLines = maxLines;
    }

    @SuppressWarnings("unused")
    @PluginFactory
    public static JTextAreaAppender createAppender(@PluginAttribute("name") String name,
                                                   @PluginAttribute("maxLines") int maxLines,
                                                   @PluginAttribute("ignoreExceptions") boolean ignoreExceptions,
                                                   @PluginElement("Layout") Layout<?> layout,
                                                   @PluginElement("Filters") Filter filter)
    {
        if (name == null)
        {
            LOGGER.error("No name provided for JTextAreaAppender");
            return null;
        }

        if (layout == null)
        {
            layout = createDefaultLayout();
        }
        return new JTextAreaAppender(name, layout, filter, maxLines, ignoreExceptions);
    }

    // Add the target JTextArea to be populated and updated by the logging information.
    public static void addLog4j2TextAreaAppender(final JTextArea textArea)
    {
        JTextAreaAppender.textAreas.add(textArea);
    }

    @Override
    public void append(LogEvent event)
    {
        String message = new String(this.getLayout().toByteArray(event));

        // Append formatted message to text area using the Thread.
        try
        {
            invokeLater(() ->
            {
                for (JTextArea textArea : textAreas)
                {
                    try
                    {
                        if (textArea != null)
                        {
                            if (textArea.getText().length() == 0)
                            {
                                textArea.setText(message);
                            } else
                            {
                                textArea.append("\n" + message);
                                if (maxLines > 0 & textArea.getLineCount() > maxLines + 1)
                                {
                                    int endIdx = textArea.getDocument().getText(0, textArea.getDocument().getLength()).indexOf("\n");
                                    textArea.getDocument().remove(0, endIdx + 1);
                                }
                            }
                            String content = textArea.getText();
                            textArea.setText(content.substring(0, content.length() - 1));
                        }
                    } catch (Throwable throwable)
                    {
                        throwable.printStackTrace();
                    }
                }
            });
        } catch (IllegalStateException exception)
        {
            exception.printStackTrace();
        }
    }
}

log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" packages="">
    <Properties>
        <Property name="log-path">log</Property>
    </Properties>
    <Appenders>
        <Console name="console-log" target="SYSTEM_OUT">
            <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"/>
        </Console>
        <JTextAreaAppender name="jtextarea-log" maxLines="100">
            <PatternLayout>
                <pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss} %msg%n</pattern>
            </PatternLayout>
        </JTextAreaAppender>
    </Appenders>
    <Loggers>
        <Logger name="My Logger" level="debug" additivity="false">
            <appender-ref ref="console-log" level="debug"/>
            <appender-ref ref="jtextarea-log" level="debug"/>
        </Logger>
        <Root level="info" additivity="false">
            <AppenderRef ref="console-log"/>
        </Root>
    </Loggers>
</Configuration>

将以下行添加到定义 GUI 的应用程序代码中,在此示例中添加到类的构造函数中MyClass:

protected static Logger logger;

public MyClass() {
      // Setup logger
      logger = LogManager.getLogger("My Logger");

      ...

      // Create logging panel
      JTextArea jLoggingConsole = new JTextArea(5,0); // 5 lines high here
      jLoggingConsole.setLineWrap(true);
      jLoggingConsole.setWrapStyleWord(true);
      jLoggingConsole.setEditable (false);
      jLoggingConsole.setFont(new Font("Courier", Font.PLAIN, 12));

      // Make scrollable console pane
      JScrollPane jConsoleScroll = new JScrollPane(this.jLoggingConsole);
      jConsoleScroll.setVerticalScrollBarPolicy ( ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS );

      // Subscribe the text area to JTextAreaAppender
      JTextAreaAppender.addLog4j2TextAreaAppender(this.jLoggingConsole);

      ...
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 Log4j2 将日志输出到 JTextArea 的相关文章

  • 无法让 Karaf 4.2.6 使用 log4j2 和 JsonLayout 作为布局类型进行日志记录

    我一整天都在做这件事 但在尝试了这么多组合后却没有让它发挥作用 归根结底 我正在寻找从 Karaf 获取 JSON 日志记录的明确步骤列表 我什至浏览了 Maven Karaf 插件源代码 试图解决这个问题 尽管也许我看的还不够远 我正在使
  • log4j2 在自定义 Appender 中将 printObject 设置为 true

    HERE https logging apache org log4j 2 x manual extending html它说 如果 toString 方法 附加程序应将 printObject 指定为 true 呈现传递给 Appende
  • 如何缓解 Apache Log4j 反序列化 RCE (CVE-2019-17571)

    我已将 log4j core 依赖项升级到 2 15 0 以防止任何潜在的 Log4Shell 攻击 话虽如此 我无法从 1 2 17 升级 slf4j log4j12 的间接 log4j 依赖项 因为 slf4j log4j12 的最新稳
  • 如何使用嵌入的清单作为资源? (Windows XP/Vista 风格的控件)

    我有一个作为 Windows 资源嵌入的清单 但正确的应用方法是什么 进一步来说 我有我的 manifest 文件 这是使应用程序使用 Windows XP Vista 视觉样式中的控件的标准方法 当通过与 exe 位于同一目录并适当命名来
  • Django 可重用应用程序配置

    我有一些连接到数据库的 Django 中间件代码 我想将中间件变成可重用的应用程序 app 这样我就可以将其打包以分发到许多其他项目中 而无需复制和粘贴 我不明白可重用应用程序应该在哪里配置自身 由于它是用于重新分发的 所以我无法自己编写中
  • 并发 log4j

    我有自己的日志引擎 它将日志写入带有阻塞队列的单独线程上 为了使用 标准软件 我正在考虑切换到 log4j 我不希望我的高并发软件因日志命令而变慢 这些日志命令在调用命令时将所有内容写入磁盘 log4j 可以用作垃圾箱吗 Log4j 是大多
  • 使用端口 80 (Ubuntu / Linode) 运行 Node.js 的最佳实践 [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我正在设置我的第一个Node js服务器上的cloud Linux node我对以下细节还很陌生Linux admin 顺便说一句 我并没有尝试
  • 自定义 web.config 部分处理程序

    我之前设计过一个自定义部分处理程序 但我遇到了一个我似乎无法想到的问题 我有一个像这样的配置部分
  • Spring - 捕获bean创建异常

    我想在我的代码中捕获 bean 实例化异常 我有什么选择 一种方法是使用基于 Java 的容器配置 Configuration public class AppConfig Bean public SomeBean someBean try
  • Logback 附加程序将消息作为 HTTP 消息发布

    根据我的要求 我只想将 HTTP 消息发布到另一端 该消息由org slf4j LoggerFactory getLogger 以下 JSON 字符串记录在INFO level studentName My Name Deratment C
  • 一次向字符串添加一行

    我有这个程序 可以让你打开一个文件并将其读入一个JTextArea使用以下代码一次性完成所有操作 try String fileContents new Scanner new File fileName useDelimiter Z ne
  • 企业库 CacheFactory.GetCacheManager 抛出空引用

    我正在尝试将使用 1 1 版本的企业库缓存块的应用程序转换为 2 0 版本 我认为我真正遇到的问题是不同 EntLib 部分的配置被分成几个文件 显然 这曾经是由ConfigurationManager 部分处理程序 但现在已经过时 取而代
  • 在 CakePHP 中访问 Configuration::read 控制器

    我的 CakePHP 应用程序有一个单独的配置文件 该文件加载在 bootstrap php 中 我的问题是 如何访问控制器中的配置变量 IE 如何在控制器中执行Configure read variable 函数 谢谢 在我的自定义配置文
  • 如何在 JTextArea 中查找光标位置

    有人会帮我找到 JTextArea 中以像素为单位的光标位置吗 我用过txtArea getCaretPosition 但这不是我所期望的位置 实际上我想要光标位置在x y像素坐标 TextUI http download oracle c
  • 为什么 StringValidator 对于自定义配置部分总是失败?

    我通过继承在 C 类库中创建了一个自定义配置部分ConfigurationSection 我在 Web 应用程序 也包括 C ASP NET 中引用了类库 填写了适当的属性 一切都运行良好 当我开始添加验证器时 问题就开始了 例如 这个属性
  • Spark 执行器 STDOUT 到 Kubernetes STDOUT

    我在 Spark Worker 中运行的 Spark 应用程序将执行程序日志输出到特定文件路径 worker home directory app xxxxxxxx 0 stdout I used log4j properties将日志从
  • 当应用程序名称在 InfoPlist.strings 中本地化时,不同的应用程序名称取决于配置

    我们为每个配置使用具有不同 plist 的设置 像这样 目标 Info Dev plist 目标 Info Beta plist 这样我们的配置就可以拥有自己的 CFBundleDisplayName 并且我们可以通过设备上的应用程序名称来
  • 无法使用前导 ../ 在顶级目录之上退出

    我有一个 asp net 网站 我们有管理区域 其中的登录页面仅供管理员使用 并且所有网站都允许所有人使用 当我收到此错误时 我需要询问如何为其定义正确的安全配置 Cannot use a leading to exit above the
  • 更改 JTextPane 的大小

    我是Java新手 刚刚在StackOverflow中找到了这段代码 ResizeTextArea https stackoverflow com questions 9370561 enabling scroll bars when jte
  • Akka :: 调度程序 [%name%] 未配置,使用默认调度程序

    我创建了以下 application conf akka actor prio dispatcher type Dispatcher mailbox type my package PrioritizedMailbox 当转储配置时 act

随机推荐