Redirect Jersey JUL logging to Log4j2

2019-05-14 07:04发布

问题:

I need to redirect Jersey request/response log to my log4j2.

I have Jersey logging enabled by using this code on my ApplicationJAXRS extends Application:

@Override
public Set<Class<?>> getClasses() {
    return new HashSet<Class<?>>() {{
        add(LoggingFilter.class);
    }};
    }

It seems that Jersey uses JUL (Java Logging) internally and the default output is STDOUT. At this moment I can see the STDOUT on Eclipse Console.

The Log4j2 documentation have a section about JDK Logging Adapter. It says

To use the JDK Logging Adapter, you must set the system property java.util.logging.manager to org.apache.logging.log4j.jul.LogManager

This must be done either through the command line (i.e., using the -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager argument) or by using System.setProperty() before any calls are made to LogManager or Logger.

To call the System.setProperty(*) before any Logger call I've tried to put it on a @PostConstruct in my Aplication class.

@PostConstruct
    public void init() {
        System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager");
    }

I could get it to log on my logging files.

This is my log4j2.xml:

    <Appenders>
        <RollingFile name="RollingFile" fileName="${log-path}/${name}.log" 
            filePattern="${log-path}/${date:yyyy-MM}/${name}-%d{yyyy-MM-dd}-%i.log">
            <PatternLayout>
                <Pattern>%d{dd-MM-yy HH:mm:ss,SSS} %-5p [%t] (%F:%L) - %m%n</Pattern>
            </PatternLayout>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
            </Policies>
            <DefaultRolloverStrategy max="10" />
        </RollingFile>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n" />
        </Console>
    </Appenders>
    <Loggers>
        <Root level="debug">
            <AppenderRef ref="Console" level="debug"/>
            <AppenderRef ref="RollingFile" level="debug"/>
        </Root>
    </Loggers>
</Configuration>

回答1:

I would imagine that you have to set the system property at launch. Add code to your init method to see if setting the system property worked.

@PostConstruct
public void init() {
    String cn = "org.apache.logging.log4j.jul.LogManager";
    System.setProperty("java.util.logging.manager", cn);
    LogManager lm = LogManager.getLogManager();
    if (!cn.equals(lm.getClass().getName())) {
       try {
           ClassLoader.getSystemClassLoader().loadClass(cn);
       } catch (ClassNotFoundException cnfe) {
          throw new IllegalStateException("Jars not in system class path.", cnfe);
       }
       throw new IllegalStateException("Found " + lm.getClass().getName() + " set as launch param instead.");
    }
}


回答2:

This actually works for the Jersey case though you still need to set the System property java.util.logging.manager=org.apache.logging.log4j.jul.LogManager

Instantiate your logger as normal for log4j2 for your general application logging. Then, explicitly instantiate a java.util.logging.Logger using org.apache.logging.log4j.jul.LogManager. This second Logger instance is then used to register a new LoggingFilter as can be seen here, using Jersey 2 Client for this example (verbose naming for clarity):

org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger(this.getClass().getName());

java.util.logging.Logger jerseyLogger = org.apache.logging.log4j.jul.LogManager.getLogManager().getLogger(this.getClass().getName());

jerseyLogger.setLevel(java.util.logging.Level.SEVERE); //OPTIONAL

Client client = ClientBuilder.newClient();
client.register(new LoggingFilter(jerseyLogger, false));

logger.info("App logging uses the normal logger");

Now, calls to logger work as expected and all of Jersey's jul logging output is directed back into log4j2 appenders as expected.