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

> 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.
>
>
True or not, nothing prevents to run it in a logger classloader to isolate
from webapp. That said if you want tomcat to log with log4j2 you likely
also want the webapps to do the same IMO. Also tomcat has a classloader
context selector so config can be per webapp or at least hook is there for
that.


> 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.
>
>
the properties config enforces it. It can get more config love but think it
already works not bad.


> 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.logg
> ing.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.logMessag
> e(ExtendedLoggerWrapper.java:217)
> org.apache.logging.log4j.spi.AbstractLogger.logMessage(Abstr
> actLogger.java:1993)
> org.apache.logging.log4j.spi.AbstractLogger.logIfEnabled(Abs
> tractLogger.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