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

时间:2014-06-03 01:25:13

标签: configuration log4j jtextarea log4j2 appender

我一直试图将日志输出到JTextArea几天,但仍然没有运气。基本上我尝试过的是在现有的appender(如consoleAppender)之后创建自己的自定义appender,并尝试在log4j2.xml中配置它。我觉得我正朝着正确的方向前进,但不知怎的,我无法让它发挥作用。我在log4j2用户邮件列表中询问过,似乎没有人关心帮助我。希望我能在这里得到我的帮助。如果您知道如何实现它,请给我步骤甚至代码片段。

感谢您的帮助。

好吧,因为有人下来投了我的问题,因为它没有显示任何努力,所以我最好展示一些。我没有表现出我所做的任何事情,因为我不太确定我所做的是正确的方式,人们可能有自己的方法。

我面临的问题是,

  • 我找不到将JTextArea对象传递给TextAreaAppender的方法
  • 当我尝试运行测试类时,总是收到错误说TextAreaAppender CLASS_NOT_FOUND,但我已经尝试了所有可能的方法来指定log4j2.xml中的class属性

代码如下,

TextAreaAppender

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>

3 个答案:

答案 0 :(得分:9)

派对有点晚了,但希望这对那些希望实现与OP相同目标的人有用。以下方法对我有用,我基于log2j解决方案提供了here以及有关log4j2 appenders here的其他一般信息。

<强> JTextAreaAppender.java

import java.util.ArrayList;

import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

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 org.apache.logging.log4j.core.layout.PatternLayout;

@Plugin(name = "JTextAreaAppender", category = "Core", elementType = "appender", printObject = true)
public class JTextAreaAppender extends AbstractAppender {

    private static final long serialVersionUID = 1L;
    private static volatile ArrayList<JTextArea> jTextAreaList = new ArrayList<JTextArea>();

    private int maxLines = 0;

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

    @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 = PatternLayout.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 addTextArea(final JTextArea textArea) {
        JTextAreaAppender.jTextAreaList.add(textArea);
    }

    @Override
    public void append(LogEvent event) {
        // TODO Auto-generated method stub
        final String message = new String(this.getLayout().toByteArray(event));

        // Append formatted message to text area using the Thread.
        try {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    for (JTextArea jTA : jTextAreaList){
                        try {
                            if (jTA != null) {
                                if (jTA.getText().length() == 0) {
                                    jTA.setText(message);
                                } else {
                                    jTA.append("\n" + message);
                                    if (maxLines > 0 & jTA.getLineCount() > maxLines + 1) {
                                        int endIdx = jTA.getDocument().getText(0, jTA.getDocument().getLength()).indexOf("\n", 0);
                                        jTA.getDocument().remove(0, endIdx+1);
                                    }
                                }
                                String content = jTA.getText();
                                jTA.setText(content.substring(0,content.length()-1));
                            }
                        } catch (final Throwable t) {
                            System.out.println("Unable to append log to text area: "
                                    + t.getMessage());
                        }
                    }
                }
            });
        } catch (final IllegalStateException e) {
            // ignore case when the platform hasn't yet been iniitialized
        }
    }
}

确保在 log4j2.xml 中的 Configuration 下设置JTextAreaAppender的

<强> log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" packages="package of JTextAreaAppender">
    <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.addTextArea(this.jLoggingConsole);

      ...
}

答案 1 :(得分:1)

您可以将Log4j中的日志输出到临时文件或内存缓冲区,并将此文件拖放到JTextArea中。我建议你不要直接登录JTextArea。日志记录的级别非常低,如果您有这样的图层,这样做会将您的用户界面元素与业务和数据库层结合起来。

答案 2 :(得分:0)

log4j2.xml

中尝试关注
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" packages="testing.Log4j2Example">
    <Appenders>
        <Console name="CONSOLE" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        </Console>
        <TextArea name="TextArea">
            <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>

我不确定,但我认为 TextAreaAppender 类需要以下行:

@Plugin(name = "TextArea", category = "Core", elementType = "appender", printObject = true)
public class TextAreaAppender extends AbstractOutputStreamAppender<OutputStreamManager>{
....
}


这个设置对我有用:)