Am 25.10.2016 um 17:07 schrieb Romain Manni-Bucau:
This has the issue but the fix is easy and 100% belonging to the Log impl
jar:
http://svn.apache.org/repos/asf/openwebbeans/microwave/trunk/microwave-core/src/main/java/org/apache/microwave/logging/log4j2/MicrowaveLogEventFactory.java
and
http://svn.apache.org/repos/asf/openwebbeans/microwave/trunk/microwave-core/src/main/resources/log4j2.component.properties


Romain Manni-Bucau
@rmannibucau <https://twitter.com/rmannibucau> |  Blog
<https://blog-rmannibucau.rhcloud.com> | Old Wordpress Blog
<http://rmannibucau.wordpress.com> | Github <https://github.com/rmannibucau> |
LinkedIn <https://www.linkedin.com/in/rmannibucau> | Tomitriber
<http://www.tomitribe.com> | JavaEE Factory
<https://javaeefactory-rmannibucau.rhcloud.com>

2016-10-25 16:38 GMT+02:00 Rainer Jung <rainer.j...@kippdata.de>:

Am 25.10.2016 um 15:33 schrieb Romain Manni-Bucau:

Hi guys,

since now tomcat has Log API as a SPI doing 2 is easy (
http://svn.apache.org/repos/asf/openwebbeans/microwave/trunk
/microwave-core/src/main/java/org/apache/microwave/logging/
tomcat/Log4j2Log.java)
and
just a drop-in jar setup so not sure it needs to be in tomcat default
delivery.


I tried to plug in Log4j2Log.java using SPI. It gets used, but it doesn't
fix the problem. Now the location info points to Log4j2Log instead of
DirectJDKLog. The Problem of one indirection layer to much above Log4Js
has't changed.

I think the jul log bridge removes one frame, which is the jul loger,
ending up taking the DirectJDKLog which vcalls the jul logger as its
location. Using your approach does no longer use jul, so log4j2 doesn't re
move a frame and again takes the wrong class Log4j2Log as its location info.

I think delegating is not enough, you actually need to implement the
target API or extend a target implementation.

Did you check the location info using the SPI approach?

Regards,

Rainer

Thanks for that hint. Actually I hadn't noticed this trick to overwrite the getSource() logic.

I'm not sure though, how dangerous this is due to possible effects on Log4J2 in some webapp. IMHO the alternative LogEvent and LogEventFactory impl must be made available for Tomcat use already in the CLASSPATH. The server loader could already be too late. That means it is also visible to any webapp. And activating it is done either via a system property, or a resource named log4j2.component.properties which contains the property

Log4jLogEventFactory=org.apache.juli.JuliLogEventFactory

Again this resource IMHO must reside in the CLASSPATH. So both ways of activating the alternative LogEvent impl also activate it for any Log4J2 in any webapp.

Although the code of the LogEvent looks compatible with the standard one, this is only true as long as we consider Log4jLogEvent being the standard one. Unfortunately the Log4J2 class LoggerConfig can use DefaultLogEventFactory (which indeed uses Log4jLogEvent) but also ReusableLogEventFactory, which uses thread-local MutableLogEvent.

So I'm not sure how safe this replacement is.

For anyone who wants to experiment here's what I did:

- create file log4j2.component.properties with the single line:

Log4jLogEventFactory=org.apache.juli.JuliLogEventFactory

- create class JuliLogEventFactory.java very similar to the one Romain created. I'll paste it inline further down.

- Put the class and the properties file into some new jar (tomcat-juli.jar should also work)

- Add the new jar and from Log4J2 log4j-api.jar, log4j-core.jar and log4j-jul.jar to the CLASSPATH

- Add a Log4J2 config (I have added its location to CLASSPATH)

- Set

LOGGING_MANAGER="-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager"

As a result the logged location info now works although the jul bridge is being used. But as said above, I'm not so sure about dangers for webapp provided Log4J2. Note that not using the jul bridge by Romain's SPI idea still needs a very similar location info fix. So we can split the discussion about how to fix location info from the discussion whether we prefer the jul bridge or a tomcat provided Log4J2 logger via SPI.

For the sake of completeness, here's the call stack we need to handle to find the location info when using the Log4J2 jul bridge:

...
org.apache.logging.log4j.core.Logger.logMessage(Logger.java:147)
org.apache.logging.log4j.spi.ExtendedLoggerWrapper.logMessage(ExtendedLoggerWrapper.java:217)
org.apache.logging.log4j.spi.AbstractLogger.logMessage(AbstractLogger.java:1993)
org.apache.logging.log4j.spi.AbstractLogger.logIfEnabled(AbstractLogger.java:1852)
org.apache.logging.log4j.jul.WrappedLogger.log(WrappedLogger.java:50)
org.apache.logging.log4j.jul.ApiLogger.log(ApiLogger.java:112)
org.apache.logging.log4j.jul.ApiLogger.logp(ApiLogger.java:132)
org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:179)
org.apache.juli.logging.DirectJDKLog.info(DirectJDKLog.java:122)
-> here comes the wanted location
...

Regards,

Rainer

Class org.apache.juli.JuliLogEventFactory:

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.juli;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.impl.DefaultLogEventFactory;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
import org.apache.logging.log4j.message.Message;

import java.util.List;

public class JuliLogEventFactory extends DefaultLogEventFactory {
    @Override
public LogEvent createEvent(final String loggerName, final Marker marker, final String fqcn, final Level level, final Message data, final List<Property> properties,
                                final Throwable t) {
return new JuliLog4jLogEvent(loggerName, marker, fqcn, level, data, properties, t);
    }

    public static class JuliLog4jLogEvent extends Log4jLogEvent {
private static final String juliLoggerName = "org.apache.juli.logging.DirectJDKLog";

        private StackTraceElement source = null;

public JuliLog4jLogEvent(final String loggerName, final Marker marker, final String fqcn, final Level level, final Message data, final List<Property> properties, final Throwable t) {
            super(loggerName, marker, fqcn, level, data, properties, t);
        }

        /**
* Returns the StackTraceElement for the caller. This will be the entry that occurs right
         * before the first occurrence of FQCN as a class name.
         * @return the StackTraceElement for the caller.
         */
        @Override
        public StackTraceElement getSource() {
            if (source != null) {
                return source;
            }
            String loggerFqcn = getLoggerFqcn();
            if (loggerFqcn == null || !isIncludeLocation()) {
                return null;
            }
            source = calcLocation(loggerFqcn);
            return source;
        }

public static StackTraceElement calcLocation(final String fqcnOfLogger) {
            if (fqcnOfLogger == null) {
                return null;
            }
// LOG4J2-1029 new Throwable().getStackTrace is faster than Thread.currentThread().getStackTrace(). final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
            StackTraceElement last = null;
            for (int i = stackTrace.length - 1; i > 0; i--) {
                final String className = stackTrace[i].getClassName();
if (fqcnOfLogger.equals(className) || juliLoggerName.equals(className)) {
                    return last;
                }
                last = stackTrace[i];
            }
            return null;
        }
    }
}

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to