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