CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-apache-logging-log4j--log4j-core

A versatile, industrial-grade, and reference implementation of the Log4j API with rich components for various logging use cases.

Pending
Overview
Eval results
Files

plugins.mddocs/

Plugin System

Log4j Core provides an extensible plugin architecture that allows custom appenders, layouts, filters, and other components to be seamlessly integrated into the logging framework. The plugin system uses annotation-based registration and factory methods for component creation.

Capabilities

Plugin Annotations

Core annotations for creating plugins that integrate with Log4j Core.

@Plugin

/**
 * Marks a class as a Log4j plugin
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Plugin {
    /**
     * Plugin name used in configuration
     * @return Plugin name
     */
    String name();
    
    /**
     * Plugin category (e.g., "Core", "Lookup", "Converter")
     * @return Plugin category
     */
    String category();
    
    /**
     * Element type for configuration (e.g., "appender", "layout", "filter")
     * @return Element type
     */
    String elementType() default "";
    
    /**
     * Whether to print object representation
     * @return Print object flag
     */
    boolean printObject() default false;
    
    /**
     * Whether to defer children processing
     * @return Defer children flag
     */
    boolean deferChildren() default false;
}

@PluginFactory

/**
 * Marks a static method as the factory method for creating plugin instances
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)  
public @interface PluginFactory {
    // No attributes - just marks the factory method
}

@PluginAttribute

/**
 * Marks a parameter as receiving an attribute value from configuration
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface PluginAttribute {
    /**
     * Attribute name in configuration
     * @return Attribute name (defaults to parameter name)
     */
    String value() default "";
    
    /**
     * Default value if attribute not specified
     * @return Default value
     */
    String defaultValue() default "";
    
    /**
     * Whether this attribute contains sensitive information
     * @return Sensitive flag
     */
    boolean sensitive() default false;
}

@PluginElement

/**
 * Marks a parameter as receiving a child element from configuration
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface PluginElement {
    /**
     * Element name in configuration
     * @return Element name (defaults to parameter name)
     */
    String value() default "";
}

@PluginConfiguration

/**
 * Marks a parameter to receive the current Configuration instance
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface PluginConfiguration {
    // No attributes - injects current configuration
}

@PluginNode

/**
 * Marks a parameter to receive the configuration Node
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface PluginNode {
    // No attributes - injects configuration node
}

@PluginLoggerContext

/**
 * Marks a parameter to receive the LoggerContext instance
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface PluginLoggerContext {
    // No attributes - injects logger context
}

Plugin Utilities

Utility classes for plugin management and discovery.

PluginManager

/**
 * Manager for plugin discovery and instantiation
 */
public class PluginManager {
    /**
     * Create PluginManager for specific category
     * @param category Plugin category to manage
     */
    public PluginManager(String category);
    
    /**
     * Get plugin type by name
     * @param name Plugin name
     * @return PluginType instance or null
     */
    public PluginType<?> getPluginType(String name);
    
    /**
     * Get all plugin types in this category
     * @return Map of plugin names to types
     */
    public Map<String, PluginType<?>> getPlugins();
    
    /**
     * Create plugin instance
     * @param name Plugin name
     * @param elementType Expected element type
     * @param node Configuration node
     * @param configuration Current configuration
     * @return Plugin instance or null
     */
    public Object createPluginObject(String name, String elementType, Node node, Configuration configuration);
}

PluginType

/**
 * Represents a plugin type with metadata
 * @param <T> Plugin class type
 */
public class PluginType<T> {
    /**
     * Get plugin class
     * @return Plugin class
     */
    public Class<T> getPluginClass();
    
    /**
     * Get plugin name
     * @return Plugin name
     */
    public String getKey();
    
    /**
     * Get element type
     * @return Element type
     */
    public String getElementType();
    
    /**
     * Get plugin category
     * @return Plugin category
     */
    public String getCategory();
    
    /**
     * Check if object should be printed
     * @return Print object flag
     */
    public boolean isObjectPrintable();
    
    /**
     * Check if children should be deferred
     * @return Defer children flag  
     */
    public boolean isDeferChildren();
}

Creating Custom Plugins

Custom Appender Example

/**
 * Example custom appender plugin
 */
@Plugin(name = "MyCustom", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE)
public class MyCustomAppender extends AbstractAppender {
    
    /**
     * Private constructor - use factory method
     */
    private MyCustomAppender(String name, Filter filter, Layout<? extends Serializable> layout,
                           boolean ignoreExceptions, String customAttribute) {
        super(name, filter, layout, ignoreExceptions, Property.EMPTY_ARRAY);
        // Initialize custom appender
    }
    
    /**
     * Append log event to custom destination
     * @param event LogEvent to append
     */
    @Override
    public void append(LogEvent event) {
        // Custom append logic
        byte[] data = getLayout().toByteArray(event);
        // Write data to custom destination
    }
    
    /**
     * Factory method for creating appender instances
     * @param name Appender name from configuration
     * @param layout Layout component from configuration
     * @param filter Filter component from configuration  
     * @param customAttribute Custom attribute value
     * @param ignoreExceptions Whether to ignore exceptions
     * @param configuration Current configuration
     * @return MyCustomAppender instance
     */
    @PluginFactory
    public static MyCustomAppender createAppender(
            @PluginAttribute("name") String name,
            @PluginElement("Layout") Layout<? extends Serializable> layout,
            @PluginElement("Filter") Filter filter,
            @PluginAttribute("customAttribute") String customAttribute,
            @PluginAttribute(value = "ignoreExceptions", defaultValue = "true") boolean ignoreExceptions,
            @PluginConfiguration Configuration configuration) {
        
        if (name == null) {
            LOGGER.error("No name provided for MyCustomAppender");
            return null;
        }
        
        if (layout == null) {
            layout = PatternLayout.createDefaultLayout();
        }
        
        return new MyCustomAppender(name, filter, layout, ignoreExceptions, customAttribute);  
    }
}

Custom Layout Example

/**
 * Example custom layout plugin
 */
@Plugin(name = "MyCustomLayout", category = Core.CATEGORY_NAME, elementType = Layout.ELEMENT_TYPE)
public class MyCustomLayout extends AbstractStringLayout {
    
    private final String prefix;
    private final String suffix;
    
    /**
     * Private constructor - use factory method
     */
    private MyCustomLayout(Charset charset, String prefix, String suffix) {
        super(charset);
        this.prefix = prefix != null ? prefix : "";
        this.suffix = suffix != null ? suffix : "";
    }
    
    /**
     * Format log event to string
     * @param event LogEvent to format
     * @return Formatted string
     */
    @Override
    public String toSerializable(LogEvent event) {
        StringBuilder sb = new StringBuilder();
        sb.append(prefix);
        sb.append(event.getTimeMillis()).append(" | ");
        sb.append(event.getLevel()).append(" | ");
        sb.append(event.getLoggerName()).append(" | ");
        sb.append(event.getMessage().getFormattedMessage());
        sb.append(suffix);
        sb.append(System.lineSeparator());
        return sb.toString();
    }
    
    /**
     * Factory method for creating layout instances
     * @param charset Character encoding
     * @param prefix Prefix string for each log entry
     * @param suffix Suffix string for each log entry
     * @return MyCustomLayout instance
     */
    @PluginFactory
    public static MyCustomLayout createLayout(
            @PluginAttribute(value = "charset", defaultValue = "UTF-8") Charset charset,
            @PluginAttribute("prefix") String prefix,
            @PluginAttribute("suffix") String suffix) {
        
        return new MyCustomLayout(charset, prefix, suffix);
    }
}

Custom Filter Example

/**
 * Example custom filter plugin
 */
@Plugin(name = "MyCustomFilter", category = Core.CATEGORY_NAME, elementType = Filter.ELEMENT_TYPE)
public class MyCustomFilter extends AbstractFilter {
    
    private final String requiredProperty;
    
    /**
     * Private constructor - use factory method
     */
    private MyCustomFilter(String requiredProperty, Result onMatch, Result onMismatch) {
        super(onMatch, onMismatch);
        this.requiredProperty = requiredProperty;
    }
    
    /**
     * Filter log event based on custom criteria
     * @param event LogEvent to filter
     * @return Filter result
     */
    @Override
    public Result filter(LogEvent event) {
        // Custom filtering logic
        String propertyValue = event.getContextData().getValue(requiredProperty);
        if (propertyValue != null && !propertyValue.isEmpty()) {
            return onMatch;
        }
        return onMismatch;
    }
    
    /**
     * Factory method for creating filter instances
     * @param requiredProperty Property that must be present
     * @param match Result when filter matches
     * @param mismatch Result when filter doesn't match
     * @return MyCustomFilter instance
     */
    @PluginFactory
    public static MyCustomFilter createFilter(
            @PluginAttribute("requiredProperty") String requiredProperty,
            @PluginAttribute(value = "onMatch", defaultValue = "NEUTRAL") Result match,
            @PluginAttribute(value = "onMismatch", defaultValue = "DENY") Result mismatch) {
        
        if (requiredProperty == null) {
            LOGGER.error("requiredProperty is required for MyCustomFilter");
            return null;
        }
        
        return new MyCustomFilter(requiredProperty, match, mismatch);
    }
}

Custom Lookup Example

/**
 * Example custom lookup plugin for variable substitution
 */
@Plugin(name = "mycustom", category = StrLookup.CATEGORY)
public class MyCustomLookup implements StrLookup {
    
    private static final Map<String, String> customData = new HashMap<>();
    
    static {
        customData.put("appname", "MyApplication");
        customData.put("version", "1.0.0");
        customData.put("environment", "production");
    }
    
    /**
     * Look up value by key
     * @param key Lookup key
     * @return Value for key or null
     */
    @Override
    public String lookup(String key) {
        return customData.get(key);
    }
    
    /**
     * Look up value with LogEvent context
     * @param event LogEvent for context
     * @param key Lookup key
     * @return Value for key or null
     */
    @Override
    public String lookup(LogEvent event, String key) {
        // Can use log event for context-specific lookups
        return lookup(key);
    }
    
    /**
     * Factory method for creating lookup instances
     * @return MyCustomLookup instance
     */
    @PluginFactory
    public static MyCustomLookup createLookup() {
        return new MyCustomLookup();
    }
}

Plugin Configuration Usage

Once plugins are created, they can be used in configuration files:

XML Configuration

<Configuration status="WARN" packages="com.example.plugins">
    <Appenders>
        <MyCustom name="Custom" customAttribute="value">
            <MyCustomLayout prefix="[CUSTOM] " suffix=" [END]"/>
            <MyCustomFilter requiredProperty="userId" onMatch="ACCEPT" onMismatch="DENY"/>
        </MyCustom>
    </Appenders>
    <Loggers>
        <Root level="INFO">
            <AppenderRef ref="Custom"/>
        </Root>
    </Loggers>
</Configuration>

Using Custom Lookup

<Configuration status="WARN">
    <Properties>
        <Property name="filename">logs/${mycustom:appname}-${mycustom:environment}.log</Property>
    </Properties>
    <Appenders>
        <File name="File" fileName="${filename}">
            <PatternLayout pattern="%d %level %logger - %msg%n"/>
        </File>
    </Appenders>
</Configuration>

Plugin Discovery and Loading

Automatic Discovery

Log4j automatically discovers plugins in:

  • org.apache.logging.log4j.core package (built-in plugins)
  • Packages specified in packages attribute of Configuration
  • Packages specified via system property log4j2.packages

Manual Plugin Loading

// Programmatically add plugin packages
System.setProperty("log4j2.packages", "com.example.plugins,com.mycompany.logging");

// Or in configuration
ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
builder.setPackages("com.example.plugins");

Plugin Categories

Standard plugin categories in Log4j Core:

  • "Core": Appenders, layouts, filters, lookups
  • "Converter": Pattern layout converters
  • "Lookup": Variable lookup providers
  • "TypeConverter": Type conversion plugins
  • "ConfigurationFactory": Configuration file parsers

Install with Tessl CLI

npx tessl i tessl/maven-org-apache-logging-log4j--log4j-core

docs

appenders.md

async-logging.md

configuration.md

core-context.md

filters.md

index.md

layouts.md

lookups.md

plugins.md

tile.json