1

I have had no sucess trying to convert this to Kotlin and use it.

It works as Java, just not with my Kotlin (which I converted using IntelliJ Kotlin Plugin)

the problem appears to be this part

 @PluginFactory
    public static AnsiConsoleAppender createAppender(

I tried to add @JvmStatic and I get this error:

Unable to invoke factory method in class AnsiConsoleAppender for element AnsiConsoleAppender: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method AnsiConsoleAppender$Companion.AnsiConsoleAppender, parameter filter java.lang.reflect.InvocationTargetException

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.appender.AppenderLoggingException;
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 java.io.Serializable;

/**
 * <h2>AnsiConsoleAppender</h2>
 *
 * <h3>notes</h3>
 * <li>class name need not match the @Plugin name</li>
 * <h3>more<h3/>
 * <li><a href="https://stackoverflow.com/a/24220688/270143">How to Create a Custom Appender in log4j2?</a></li>
 */
@Plugin(name="AnsiConsoleAppender", category="Core", elementType="appender", printObject=true)
public final class AnsiConsoleAppender extends AbstractAppender {

    protected AnsiConsoleAppender(String name, Filter filter,
                                  Layout<? extends Serializable> layout, final boolean ignoreExceptions) {
        super(name, filter, layout, ignoreExceptions);
    }

    // The append method is where the appender does the work.
    // Given a log event, you are free to do with it what you want.
    // This example demonstrates:
    // 1. Concurrency: this method may be called by multiple threads concurrently
    // 2. How to use layouts
    // 3. Error handling
    //@Override
    public void append(LogEvent event) {
        try {
            final byte[] bytes = getLayout().toByteArray(event);

// output code goes here

        } catch (Exception ex) {
            if (!ignoreExceptions()) throw new AppenderLoggingException(ex);
        }
    }

    // Your custom appender needs to declare a factory method
    // annotated with `@PluginFactory`. Log4j will parse the configuration
    // and call this factory method to construct an appender instance with
    // the configured attributes.
    @PluginFactory
    public static AnsiConsoleAppender createAppender(
            @PluginAttribute("name") String name,
            @PluginElement("Layout") Layout<? extends Serializable> layout,
            @PluginElement("Filter") final Filter filter,
            @PluginAttribute("otherAttribute") String otherAttribute) {
        if (name == null) {
            LOGGER.error("No name provided for AnsiConsoleAppenderImpl");
            return null;
        }
        if (layout == null) {
            layout = PatternLayout.createDefaultLayout();
        }
        return new AnsiConsoleAppender(name, filter, layout, true);
    }
}
import org.apache.logging.log4j.core.AbstractLifeCycle
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.appender.AppenderLoggingException
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 java.io.Serializable

@Plugin(name = "AnsiConsoleAppender", category = "Core", elementType = "appender", printObject = true)
class AnsiConsoleAppender /*protected*/ constructor(
    name: String, filter: Filter,
    layout: Layout<out Serializable>, ignoreExceptions: Boolean
) : AbstractAppender(name, filter, layout, ignoreExceptions) {

    // The append method is where the appender does the work.
    // Given a log event, you are free to do with it what you want.
    // This example demonstrates:
    // 1. Concurrency: this method may be called by multiple threads concurrently
    // 2. How to use layouts
    // 3. Error handling
    //@Override
    override fun append(event: LogEvent) {
        try {
            val bytes = layout.toByteArray(event)
            //AnsiColor.out(String(bytes), ColorMaps.ASTERIKSY, null, true)
        } catch (ex: Exception) {
            if (!ignoreExceptions()) throw AppenderLoggingException(ex)
        }

    }

    companion object {

        // Your custom appender needs to declare a factory method
        // annotated with `@PluginFactory`. Log4j will parse the configuration
        // and call this factory method to construct an appender instance with
        // the configured attributes.
        @JvmStatic
        @PluginFactory
        fun createAppender(
            @PluginAttribute("name") name: String?,
            @PluginElement("Layout") layout: Layout<out Serializable>?,
            @PluginElement("Filter") filter: Filter,
            @PluginAttribute("otherAttribute") otherAttribute: String
        ): AnsiConsoleAppender? {
            val lay = layout ?: PatternLayout.createDefaultLayout()
            if (name == null) {
                AbstractLifeCycle.LOGGER.error("No name provided for AnsiConsoleAppenderImpl")
                return null
            }
            return AnsiConsoleAppender(name, filter, lay, true)
        }
    }
}

what am I missing?

ycomp
  • 8,316
  • 19
  • 57
  • 95
  • The kotlin code you generated might depend on a platform type and assumes it to be null causing an issue at runtime. If you provide the actual kotlin code it would be a lot easier to make an evaluation of the problem at hand. – Minn Jan 26 '19 at 12:58

2 Answers2

1

The exception says filter is null.

So it should be nullable type.

companion object {
    @PluginFactory
    @JvmStatic
    fun createAppender(
            @PluginAttribute("name") name: String,
            @PluginElement("Layout") layout: Layout<out Serializable>,
            @PluginElement("Filter") filter: Filter?,
            @PluginAttribute("otherAttribute") otherAttribute: String?
    ): AnsiConsoleAppender {
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
  • ah I see that was it.. I had to do that to `filter` and then to `otherAttribute` - really stupid of me, it is obvious in the error I posted.. but I didn't read that, I was reading the console which had the name of the parameter collapsed so that confused me... :) – ycomp Jan 26 '19 at 13:40
0

In Kotlin factory methods are usually defined in a companion object of a class, e.g.:

class AnsiConsoleAppender : AbstractAppender() {
    companion object {
        // add JvmStatic annotation if you want to call this method from Java file
        @JvmStatic 
        @PluginFactory
        fun createAppender(/*params*/): AnsiConsoleAppender {
            //...
        }
    }
}

More on companion object

Sergio
  • 27,326
  • 8
  • 128
  • 149
  • thanks I had done that already, that's why I was confused why it wasn't working - turns out I just needed to read the exception better as EpicPandaForce pointed out – ycomp Jan 26 '19 at 13:41