Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Log4j2 - Lookup Plugin (StrLookup) to resolve ThreadName for routing RollingLogFile via Thread

I am trying to configure a log4j2 config to route messages to different logfiles for a multithreaded program via threadname.

Here is what I have, so far (relevant to log4j2 config):

|-/src/main/java/log4j2/plugins
|-- ThreadLookup.java
|-/src/main/resources
|-- log4j2.xml

ThreadLookup.java:

package log4j2.plugins;

import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.lookup.StrLookup;

@Plugin(name="threadLookup", category=StrLookup.CATEGORY)
public class ThreadLookup implements StrLookup {

    @Override
    public String lookup(String key) {
        return Thread.currentThread().getName();
    }

    @Override
    public  String lookup(LogEvent event, String key) {
        // Check event first:
        if (event.getThreadName() != null) {
            return event.getThreadName();
        }
        // Fallback to key if event doesn't define a threadName:
        return this.lookup(key);
    }

}

Log4j2.xml (It is my understanding that the packages attribute of Configuration should read in ThreadLookup.java and based on the annotation create a new threadLookup prefix to let me call lookup(String key) with whatever value I want to -- in this case I am not using a specific value because this class will only do a threadName lookup):

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="error" strict="true" schema="Log4J-V2.0.xsd"
               packages="log4j2.plugins">
    <Properties>
        <Property name="logMsgPattern">%date{yyyy/MM/dd HH:mm:ss.SSS} %-5level ${sys:pid}[%thread] %class %method:%line - %message%n</Property>
    </Properties>

    <Appenders>

        <Console name="console" target="SYSTEM_OUT" >
            <PatternLayout pattern="${logMsgPattern}" />
        </Console>

        <Routing name="routing">
            <Routes pattern="$${threadLookup:threadName}">
                <Route>
                    <RollingFile name="RollingFile-${threadLookup:threadName}"
                                 fileName="${sys:log4j.dir}/thread-${threadLookup:threadName}.log"
                                 filePattern="${sys:log4j.dir}/thread-${threadLookup:threadName}-%i.log.gz">
                            <PatternLayout pattern="${logMsgPattern}"/>
                            <Policies>
                                <SizeBasedTriggeringPolicy size="100 MB" />
                            </Policies>
                    </RollingFile>
                </Route>
            </Routes>
        </Routing>

        <!-- Config for other appenders snipped -->

    </Appenders>

    <Loggers>
        <!-- Config for other loggers snipped -->
        <Root level="${sys:log4j.console.threshold}">
            <AppenderRef ref="rootOut" level="trace" />
            <AppenderRef ref="rootErr" level="error" />
            <AppenderRef ref="console" level="${sys:log4j.console.threshold}" />
            <AppenderRef ref="routing" level="trace" />
        </Root>
    </Loggers>

</Configuration>

However, when I launch my app, it just creates an additional file called thread-${threadLookup (no extension) in my log directory. It also never hits any breakpoints in ThreadLookup.java.

How can I register the plugin with log4j2 (I was using version 2.2, I also tried 2.3)? Note, I am using a spring-framework 4.1.7 project, if that helps at all; I use maven for the project as well, but I am only using it to resolve dependencies, I build the project via ant script.

UPDATE

When I build the script via ant, I do actually get a Log4j2Plugins.dat that shows up in my classpath (-cp resources:bin), but it doesn't seem to effect the outcome of the logs that are generated on the server:

$ find bin/META-INF/ -type f
bin/META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat

$ cat bin/META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat
lookup
      threadlookupog4j2.plugins.ThreadLookup
                                            threadLookup

$ vi bin/META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat
^A^Flookup^A^Lthreadlookup^[log4j2.plugins.ThreadLookup^LthreadLookup

$ find logs -type f -name "thread-*"
logs/thread-${threadLookup:threadName}.log
logs/thread-${threadLookup:threadName}-1.log.gz</pre>

Thanks

like image 460
anonymous Avatar asked Jul 10 '15 14:07

anonymous


People also ask

What is JNDI lookup in log4j2?

Log4j uses a lookup feature called Java Naming and Directory Interface (JNDI) that consists of an API (Application Programming Interface) and SPI (Service Provider Interface). Several naming and directory services plug in via the SPI, namely DNS, LDAP, NIS, and more.

What is a Log4j Lookup?

Lookups provide a way to add values to the Log4j configuration at arbitrary places. They are a particular type of Plugin that implements the StrLookup interface. Information on how to use Lookups in configuration files can be found in the Property Substitution section of the Configuration page.

What is plugin in log4j2?

Log4j 2 uses plugins like Appenders and Layouts to format and output logs. These are known as core plugins, and Log4j 2 provides a lot of options for us to choose from. However, in some cases, we may also need to extend the existing plugin or even write custom ones.


1 Answers

I ended up finding out that the issue was that my Plugin name cannot be camelCased.

I was debugging through PluginManager.java (Log4j2 - 2.3), on line 169 in private static void mergeByName(final Map<String, PluginType<?>> newPlugins, final List<PluginType<?>> plugins), and I saw that I had the following properties to go into newPlugins.put(key, pluginType);

  • key: threadlookup,
  • pluginType: PluginType [pluginClass=class log4j2.plugins.ThreadLookup, key=threadlookup, elementName=threadLookup, isObjectPrintable=false, isDeferChildren==false, category=Lookup]

After seeing that, I modified my Routing appender in my log4j2.xml config to the following (without needing to change the annotation in my Plugin class that implemented StrLookup) and it worked:

    <Routing name="routing">
        <Routes pattern="$${threadlookup:threadName}">
            <Route>
                <RollingFile name="RollingFile-${threadlookup:threadName}"
                             fileName="${sys:log4j.dir}/thread-${threadlookup:threadName}.log"
                             filePattern="${sys:log4j.dir}/thread-${threadlookup:threadName}-%i.log.gz">
                        <PatternLayout pattern="${logMsgPattern}"/>
                        <Policies>
                            <SizeBasedTriggeringPolicy size="100 MB" />
                        </Policies>
                </RollingFile>
            </Route>
        </Routes>
    </Routing>

Hopefully this can help others out, as I had to spend a few days to figure this out and I didn't find this in any of the documentation or questions I was reviewing for Log4j2.

Thanks!

like image 147
anonymous Avatar answered Nov 02 '22 11:11

anonymous