JTextPaneAppender.java

package com.jsql.view.swing.console;

import com.jsql.util.LogLevelUtil;
import com.jsql.view.swing.util.UiUtil;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.*;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.Property;
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;

import javax.swing.*;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import java.awt.*;
import java.nio.charset.StandardCharsets;
import java.util.AbstractMap;
import java.util.Optional;
import java.util.stream.Stream;

/**
 * Log4j2
 * LOGGER.info(e)
 * => No query string
 * LOGGER.info(e.getMessage())
 * => com.jsql.model.exception.InjectionFailureException: No query string
 * LOGGER.info(e, e)
 * => com.jsql.model.exception.InjectionFailureException: No query string + Stacktrace
 */
@Plugin(
    name = "JTextPaneAppender",
    category = Core.CATEGORY_NAME,
    elementType = Appender.ELEMENT_TYPE,
    printObject = true
)
public class JTextPaneAppender extends AbstractAppender {
    
    // Main console
    private static SimpleConsoleAdapter consoleTextPane;
    
    // Java console
    private static JavaConsoleAdapter javaTextPane;

    public static final SimpleAttributeSet ATTRIBUTE_WARN = new SimpleAttributeSet();
    public static final SimpleAttributeSet ATTRIBUTE_INFORM = new SimpleAttributeSet();
    public static final SimpleAttributeSet ATTRIBUTE_SUCCESS = new SimpleAttributeSet();
    public static final SimpleAttributeSet ATTRIBUTE_ALL = new SimpleAttributeSet();
    
    static {
        Stream.of(
            new AbstractMap.SimpleEntry<>(ATTRIBUTE_WARN, Color.RED),
            new AbstractMap.SimpleEntry<>(ATTRIBUTE_INFORM, Color.BLUE),
            new AbstractMap.SimpleEntry<>(ATTRIBUTE_SUCCESS, UiUtil.COLOR_GREEN),
            new AbstractMap.SimpleEntry<>(ATTRIBUTE_ALL, Color.BLACK)
        )
        .forEach(entry -> {
            
            StyleConstants.setFontFamily(entry.getKey(), UiUtil.FONT_NAME_MONO_NON_ASIAN);
            StyleConstants.setFontSize(entry.getKey(), UiUtil.FONT_SIZE_MONO_NON_ASIAN);
            StyleConstants.setForeground(entry.getKey(), entry.getValue());
        });
    }
    
    private JTextPaneAppender(String name, Layout<?> layout, Filter filter, boolean ignoreExceptions) {
        super(name, filter, layout, ignoreExceptions, Property.EMPTY_ARRAY);
    }

    @SuppressWarnings({ "unused", "rawtypes" })
    @PluginFactory
    public static JTextPaneAppender createAppender(
        @PluginAttribute("name") String name,
        @PluginAttribute("ignoreExceptions") boolean ignoreExceptions,
        @PluginElement("Layout") Layout layout,
        @PluginElement("Filters") Filter filter
    ) {
        if (name == null) {
            
            LOGGER.log(LogLevelUtil.CONSOLE_JAVA, "No name provided for JTextPaneAppender");
            return null;
        }

        var layoutTextPane = Optional.ofNullable(layout).orElse(PatternLayout.createDefaultLayout());
        
        return new JTextPaneAppender(name, layoutTextPane, filter, ignoreExceptions);
    }

    @Override
    public void append(LogEvent event) {
        
        // Avoid errors which might occur in headless mode
        // or logging that occurs before consoles are available
        if (consoleTextPane == null || javaTextPane == null) {
            return;
        }
        
        var messageLogEvent = new String[] {
            new String(this.getLayout().toByteArray(event), StandardCharsets.UTF_8)
        };
        
        var level = event.getLevel().intLevel();
        
        SwingUtilities.invokeLater(() -> {
            
            String message = messageLogEvent[0];
            
            if (level == LogLevelUtil.CONSOLE_JAVA.intLevel()) {
                javaTextPane.append(message, ATTRIBUTE_WARN);
            } else if (level == LogLevelUtil.CONSOLE_ERROR.intLevel()) {
                consoleTextPane.append(message, ATTRIBUTE_WARN);
            } else if (level == LogLevelUtil.CONSOLE_INFORM.intLevel()) {
                consoleTextPane.append(message, ATTRIBUTE_INFORM);
            } else if (level == LogLevelUtil.CONSOLE_SUCCESS.intLevel()) {
                consoleTextPane.append(message, ATTRIBUTE_SUCCESS);
            } else if (level != LogLevelUtil.IGNORE.intLevel() && level != Level.ERROR.intLevel()) {  // ignore & stdout when unhandled exception
                consoleTextPane.append(message, ATTRIBUTE_ALL);
            }
        });
    }
    
    /**
     * Register the java console.
     * @param javaConsole
     */
    public static void register(JavaConsoleAdapter javaConsole) {
        JTextPaneAppender.javaTextPane = javaConsole;
    }

    /**
     * Register the default console.
     * @param consoleColored
     */
    public static void register(SimpleConsoleAdapter consoleColored) {
        JTextPaneAppender.consoleTextPane = consoleColored;
    }
}