Hi all, I have followed Ralph's suggestion in this issue and the source code of ConsoleAppender from apache to tried to create a custom appender for appending logs to a JTextArea.
https://issues.apache.org/jira/browse/LOG4J2-303 But I am having trouble to make it to work, could anyone please give me some help for this? the problems I am facing are, - I cannot find a way to pass that JTextArea object to my TextAreaAppender - I am also having problem creating the manager, in ConsoleAppender, they use "new OutputStreamManager(data.os, data.type, data.layout)", and it's actually a "protected" constructor, how is this possible? BTW, eclipse is also showing error about this. *Actually, I got around with this by creating a subclass of OutputStreamManager in my test and make the constructor public, not sure if I have done it in a right way though.* Any help would be appreciated. Test classes are as follow, *TextAreaAppender.java* package testing; import java.io.IOException; import java.io.OutputStream; import java.io.Serializable; import javax.swing.JTextArea; import org.apache.logging.log4j.core.Filter; import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender; import org.apache.logging.log4j.core.appender.ManagerFactory; import org.apache.logging.log4j.core.appender.OutputStreamManager; 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.helpers.Booleans; import org.apache.logging.log4j.core.layout.PatternLayout; import testing.ConsoleAppender.ConsoleManagerFactory; 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 OutputStreamManager(data.os, data.type, data.layout);// protected constructor??? } } } *Lo4gj2Example.java* package testing; import java.awt.BorderLayout; import java.awt.Dimension; import java.io.IOException; import java.io.OutputStream; import javax.swing.JFrame; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.border.TitledBorder; import javax.swing.table.AbstractTableModel; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.Filter; import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender; import org.apache.logging.log4j.core.appender.OutputStreamManager; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.config.plugins.PluginElement; import org.apache.logging.log4j.core.config.plugins.PluginFactory; 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 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) { logger.trace("Entering Log4j Example."); Hello hello = new Hello(); if (!hello.callMe()) { logger.error("Ohh!Failed!"); } logger.trace("Exiting Log4j Example."); INSTANCE.run(); } } *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>*