Thanks. I decided to change my approach. I am not using the previous patch
anymore.
I patched the ThreadContext (based on PAXLOGGING-244), reworked my code to
use the ThreadContext instead of modifying the logger name, and also made
some changes to the pax-logging-api to fix some of the leak issues and to
address inconsistencies between the various logging implementations. For my
pax-logging-api changes, some of it follows what was done in the 1.11.x
branch for PAXLOGGING-307. I no longer swap the order of the WeakHashMap
parameters back to the original <Logger, String>. My patch keeps it with
the new <String, Logger> parameters, but does not store Logger
implementations in the map if the Pax Logging Manager is already created
(as mentioned earlier, SLF4J already had this check, but Log4J1 did not).
I attached my two patches and the instructions I wrote so that my teammates
could build the new jars. Feel free to use them or modify them as needed.
Monica
On Tuesday, April 21, 2020 at 2:48:57 AM UTC-4, Grzegorz Grzybek wrote:
>
> Hello
>
> Sorry for big delay... I still remember about this issue and I think I can
> do something about it soon. Just a little bit patience please ;)
>
> regards
> Grzegorz Grzybek
>
> śr., 18 mar 2020 o 22:47 Monica Ron <[email protected] <javascript:>>
> napisał(a):
>
>> I have a test that shows my groups usage. Should I just attach it as a
>> part of a post to this forum? It definitely behaves differently with the
>> 1.10.5 vs. with my patch, with regards to how many logger instances get
>> stored in m_loggers (especially if I use Log4J1 vs. Log4J2 as my API).
>>
>> I use the Log4J2 API in my real code, as I've stated before (but
>> third-party code we use uses SLF4J or JCL, and maybe others). I tried to
>> use the ThreadContext in my code (instead of the Markers that Ralph
>> mentioned), and ran into trouble, because I ran into the problem described
>> in https://ops4j1.jira.com/browse/PAXLOGGING-244 , for which the fix was
>> not applied to the 1.10.5 branch. Once I backported that fix to the 1.10.5
>> branch (making a new pax-logging-api and new pax-logging-log4j2, the
>> ThreadContext worked, and I could re-use logger names and still see which
>> "group" my log statements were from.
>>
>> Even if I change my code to use ThreadContext, the memory behavior of
>> 1.10.5 with regards to m_loggers is still a leak compared to the old 1.6.1
>> we were using, as I have been stating all along.
>>
>> I think the inconsistencies with regard to the following two items
>> (mentioned in my previous post) is also an issue:
>> 1. storing values in the m_loggers maps when m_paxLogging is non-null (
>> *only* SLF4J API in pax-logging-api 1.10.5 does **not** store it if
>> m_paxLogging is non-null), and
>> 2. getting a new logger even if a name is reused vs. re-using the old
>> logger (*only* Log4J2 API in pax-logging-api 1.10.5 reuses the logger if
>> the name was already used--other implementations just keep creating new
>> loggers for the same name, and store all of those loggers in m_loggers)
>>
>> Because of #1 and #2, if I was using Log4J1 API in pax-logging-api
>> 1.10.5, then even if I re-used the name for a non-static logger, the
>> m_loggers just keeps growing. At least with Log4J2, if I re-use the name
>> for a non-static logger, the m_loggers does not grow.
>>
>> Thanks again,
>> Monica
>>
>> --
>> --
>> ------------------
>> OPS4J - http://www.ops4j.org - [email protected] <javascript:>
>>
>> ---
>> You received this message because you are subscribed to the Google Groups
>> "OPS4J" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to [email protected] <javascript:>.
>> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/ops4j/e6783b83-bc0c-4d98-aae3-d28e72949c2b%40googlegroups.com
>>
>> <https://groups.google.com/d/msgid/ops4j/e6783b83-bc0c-4d98-aae3-d28e72949c2b%40googlegroups.com?utm_medium=email&utm_source=footer>
>> .
>>
>
--
--
------------------
OPS4J - http://www.ops4j.org - [email protected]
---
You received this message because you are subscribed to the Google Groups
"OPS4J" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/ops4j/60e642dd-33d3-4249-beb4-87d2b65d7944%40googlegroups.com.
## README file for building Pax Logging patch to address memory leak issues.
##
## Requirements:
## 1. You must have 'git' installed.
## 2. You must have a connection to the internet to download the original Pax
Logging branch (using 'git') before you can apply the patch.
## 3. You must have Maven installed.
##
########
## The patch is based on code from the pax-logging-1.10.x branch of Pax Logging.
##
## To download the code and apply the patches, call the following from this
directory:
git init
git remote add origin https://github.com/ops4j/org.ops4j.pax.logging.git
git fetch origin
git merge aaa0909bdd4e1fb1179ad0beabb0520fd89af060
git apply PaxLoggingThreadContext.patch
git apply PaxLoggingMemoryLeak.patch
## Then do the following, substituting the appropriate local settings file:
mvn clean install -s localSettings.xml
## ----------------------------------
## DEPLOYMENT INSTRUCTIONS:
## The following two files need to be deployed in the Payara5
glassfish/modules/autostart directory.
## 1. pax-logging-api/target/pax-logging-api-1.10.6-SNAPSHOT.jar
## 2. pax-logging-log4j2/target/pax-logging-log4j2-1.10.6-SNAPSHOT.jar
##
## The other pax-logging-XXX projects do not need to be used.
##
## ----------------------------------
## PATCH DESCRIPTIONS:
## A. The PaxLoggingThreadContext.patch is a backport of fixes from Pax Logging
Jira issue PAXLOGGING-244. The fixes for that issue were not made in the
## official pax-logging-1.10.x branch, even though the bug is noted as
affecting Pax Logging 1.10.0.
##
## B. The PaxLoggingMemoryLeak.patch makes changes to how the individual
loggers for the various logging implementations are created. The loggers are now
## only stored in the m_loggers maps in the factories if the Pax Logging
Manager has not yet been created. Once the Pax Logging Manager is created (i.e.,
## when the org.ops4j.pax.logging.internal.Activator 'start' method is
called by the OSGi framework as part of pax-logging-api bundle activation), all
## existing loggers are updated, and then the m_loggers map in each factory
is cleared. Some of the changes in this patch follow some of what was done on
## the pax-logging-1.11.x branch for PAXLOGGING-307. Some changes for
PAXLOGGING-307 were included in the official Pax Logging 1.10.5 release, but
## inconsistencies in the different logging implementations (that is,
inconsistencies in the Pax Logging version of these implementations) meant
there were
## still memory leaks when a new logger was created.
##
diff --git a/pax-logging-api/src/main/java/org/ops4j/pax/logging/log4jv2/Log4jv2LoggerContext.java b/pax-logging-api/src/main/java/org/ops4j/pax/logging/log4jv2/Log4jv2LoggerContext.java
index 32467cc1..2288425e 100644
--- a/pax-logging-api/src/main/java/org/ops4j/pax/logging/log4jv2/Log4jv2LoggerContext.java
+++ b/pax-logging-api/src/main/java/org/ops4j/pax/logging/log4jv2/Log4jv2LoggerContext.java
@@ -34,7 +34,7 @@ public class Log4jv2LoggerContext implements LoggerContext {
private static final ConcurrentMap<String, Log4jv2Logger> loggers = new ConcurrentHashMap<String, Log4jv2Logger>();
- private static PaxLoggingManager paxLogging;
+ static PaxLoggingManager paxLogging;
public static void setBundleContext( BundleContext ctx )
{
diff --git a/pax-logging-api/src/main/java/org/ops4j/pax/logging/log4jv2/Log4jv2ThreadContextMap.java b/pax-logging-api/src/main/java/org/ops4j/pax/logging/log4jv2/Log4jv2ThreadContextMap.java
index cc911aa1..94f125d0 100644
--- a/pax-logging-api/src/main/java/org/ops4j/pax/logging/log4jv2/Log4jv2ThreadContextMap.java
+++ b/pax-logging-api/src/main/java/org/ops4j/pax/logging/log4jv2/Log4jv2ThreadContextMap.java
@@ -27,7 +27,7 @@ import org.apache.logging.log4j.spi.ThreadContextMap;
import org.ops4j.pax.logging.OSGIPaxLoggingManager;
import org.ops4j.pax.logging.PaxContext;
import org.ops4j.pax.logging.PaxLoggingManager;
-import org.osgi.framework.BundleContext;
+import org.ops4j.pax.logging.PaxLoggingService;
/**
* The actual ThreadContext Map. A new ThreadContext Map is created each time it is updated and the Map stored is always
@@ -37,17 +37,10 @@ import org.osgi.framework.BundleContext;
*/
public class Log4jv2ThreadContextMap implements ThreadContextMap {
- private static PaxContext m_context;
+ /** {@link PaxContext} used when {@link org.ops4j.pax.logging.PaxLoggingService} is not available */
private static PaxContext m_defaultContext = new PaxContext();
-
- private static PaxLoggingManager m_paxLogging;
-
- public static void setBundleContext( BundleContext ctx )
- {
- m_paxLogging = new OSGIPaxLoggingManager( ctx );
- // We need to instruct all loggers to ensure the SimplePaxLoggingManager is replaced.
- m_paxLogging.open();
- }
+ /** {@link PaxContext} obtained from {@link org.ops4j.pax.logging.PaxLoggingService} */
+ private static PaxContext m_context;
public static void dispose()
{
@@ -60,8 +53,12 @@ public class Log4jv2ThreadContextMap implements ThreadContextMap {
* or m_defaultContext if the logging manager is not set, or does not have its context available yet.
*/
private static PaxContext getContext() {
- if( m_context==null && m_paxLogging!=null ){
- m_context=(m_paxLogging.getPaxLoggingService()!=null)?m_paxLogging.getPaxLoggingService().getPaxContext():null;
+ PaxLoggingManager manager = Log4jv2LoggerContext.paxLogging;
+ if (manager != null) {
+ synchronized (Log4jv2ThreadContextMap.class) {
+ PaxLoggingService service = manager.getPaxLoggingService();
+ m_context = service != null ? service.getPaxContext() : null;
+ }
}
return m_context!=null?m_context:m_defaultContext;
}
diff --git a/pax-logging-log4j2/src/main/java/org/ops4j/pax/logging/log4j2/internal/PaxLoggerImpl.java b/pax-logging-log4j2/src/main/java/org/ops4j/pax/logging/log4j2/internal/PaxLoggerImpl.java
index 87a79ede..424013b6 100644
--- a/pax-logging-log4j2/src/main/java/org/ops4j/pax/logging/log4j2/internal/PaxLoggerImpl.java
+++ b/pax-logging-log4j2/src/main/java/org/ops4j/pax/logging/log4j2/internal/PaxLoggerImpl.java
@@ -127,7 +127,9 @@ public class PaxLoggerImpl
private void clearDelegateContext()
{
- ThreadContext.clearMap();
+ ThreadContext.remove("bundle.id");
+ ThreadContext.remove("bundle.name");
+ ThreadContext.remove("bundle.version");
}
private void doLog(final Level level, final int svcLevel, final String fqcn, final String message, final Throwable t ) {
diff --git a/pax-logging-api/src/main/java/org/apache/commons/logging/LogFactory.java b/pax-logging-api/src/main/java/org/apache/commons/logging/LogFactory.java
index 70df4d07..9d71c27e 100755
--- a/pax-logging-api/src/main/java/org/apache/commons/logging/LogFactory.java
+++ b/pax-logging-api/src/main/java/org/apache/commons/logging/LogFactory.java
@@ -113,6 +113,7 @@ public class LogFactory
}
}
m_paxLogging.open();
+ m_loggers.clear();
}
}
@@ -253,11 +254,13 @@ public class LogFactory
logger = m_paxLogging.getLogger(name, JclLogger.JCL_FQCN);
}
JclLogger jclLogger = new JclLogger(logger);
- synchronized (m_loggers) {
- if (!m_loggers.containsKey(name)) {
- m_loggers.put(name, new LinkedList<JclLogger>());
+ if (m_paxLogging == null) {
+ synchronized (m_loggers) {
+ if (!m_loggers.containsKey(name)) {
+ m_loggers.put(name, new LinkedList<JclLogger>());
+ }
+ m_loggers.get(name).add(jclLogger);
}
- m_loggers.get(name).add(jclLogger);
}
return jclLogger;
}
diff --git a/pax-logging-api/src/main/java/org/apache/juli/logging/LogFactory.java b/pax-logging-api/src/main/java/org/apache/juli/logging/LogFactory.java
index 51e35667..59863bb6 100644
--- a/pax-logging-api/src/main/java/org/apache/juli/logging/LogFactory.java
+++ b/pax-logging-api/src/main/java/org/apache/juli/logging/LogFactory.java
@@ -34,7 +34,6 @@
package org.apache.juli.logging;
-import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -157,7 +156,7 @@ public class LogFactory {
static
{
- m_loggers = Collections.synchronizedMap( new WeakHashMap<String, List<JuliLogger>>() );
+ m_loggers = new WeakHashMap<String, List<JuliLogger>>();
singleton = new LogFactory();
}
/**
@@ -201,11 +200,13 @@ public class LogFactory {
logger = m_paxLogging.getLogger( name, JuliLogger.JULI_FQCN );
}
JuliLogger juliLogger = new JuliLogger( logger );
- synchronized (m_loggers) {
- if (!m_loggers.containsKey(name)) {
- m_loggers.put(name, new LinkedList<JuliLogger>());
+ if (m_paxLogging == null) {
+ synchronized (m_loggers) {
+ if (!m_loggers.containsKey(name)) {
+ m_loggers.put(name, new LinkedList<JuliLogger>());
+ }
+ m_loggers.get(name).add(juliLogger);
}
- m_loggers.get(name).add(juliLogger);
}
return juliLogger;
}
@@ -396,6 +397,7 @@ public class LogFactory {
}
}
m_paxLogging.open();
+ m_loggers.clear();
}
}
diff --git a/pax-logging-api/src/main/java/org/apache/log4j/Logger.java b/pax-logging-api/src/main/java/org/apache/log4j/Logger.java
index a8e76f66..3c32be17 100644
--- a/pax-logging-api/src/main/java/org/apache/log4j/Logger.java
+++ b/pax-logging-api/src/main/java/org/apache/log4j/Logger.java
@@ -17,7 +17,6 @@
package org.apache.log4j;
-import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -65,7 +64,7 @@ public class Logger extends Category
static
{
- m_loggers = Collections.synchronizedMap( new WeakHashMap<String, List<Logger>>() );
+ m_loggers = new WeakHashMap<String, List<Logger>>();
}
public static void setBundleContext( BundleContext ctx )
@@ -83,6 +82,7 @@ public class Logger extends Category
}
}
m_paxLogging.open();
+ m_loggers.clear();
}
}
@@ -138,11 +138,13 @@ public class Logger extends Category
paxLogger = m_paxLogging.getLogger( name, LOG4J_FQCN );
}
Logger logger = new Logger( paxLogger );
- synchronized (m_loggers) {
- if (!m_loggers.containsKey(name)) {
- m_loggers.put(name, new LinkedList<Logger>());
+ if (m_paxLogging == null) {
+ synchronized (m_loggers) {
+ if (!m_loggers.containsKey(name)) {
+ m_loggers.put(name, new LinkedList<Logger>());
+ }
+ m_loggers.get(name).add(logger);
}
- m_loggers.get(name).add(logger);
}
return logger;
}
diff --git a/pax-logging-api/src/main/java/org/ops4j/pax/logging/avalon/AvalonLogFactory.java b/pax-logging-api/src/main/java/org/ops4j/pax/logging/avalon/AvalonLogFactory.java
index d26164bd..6effa471 100644
--- a/pax-logging-api/src/main/java/org/ops4j/pax/logging/avalon/AvalonLogFactory.java
+++ b/pax-logging-api/src/main/java/org/ops4j/pax/logging/avalon/AvalonLogFactory.java
@@ -17,7 +17,6 @@
*/
package org.ops4j.pax.logging.avalon;
-import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -28,6 +27,7 @@ import org.apache.avalon.framework.logger.Logger;
import org.ops4j.pax.logging.OSGIPaxLoggingManager;
import org.ops4j.pax.logging.PaxLogger;
import org.ops4j.pax.logging.PaxLoggingManager;
+import org.ops4j.pax.logging.internal.FallbackLogFactory;
import org.osgi.framework.BundleContext;
public class AvalonLogFactory
@@ -38,7 +38,7 @@ public class AvalonLogFactory
static
{
- m_loggers = Collections.synchronizedMap( new WeakHashMap<String, List<AvalonLogger>>() );
+ m_loggers = new WeakHashMap<String, List<AvalonLogger>>();
}
public static void setBundleContext( BundleContext context )
@@ -55,6 +55,7 @@ public class AvalonLogFactory
}
}
m_paxLogging.open();
+ m_loggers.clear();
}
}
@@ -81,13 +82,24 @@ public class AvalonLogFactory
{
newName = parent.getName() + "." + name;
}
+ PaxLogger paxLogger;
+ if( m_paxLogging == null )
+ {
+ paxLogger = FallbackLogFactory.createFallbackLog( null, name );
+ }
+ else
+ {
+ paxLogger = m_paxLogging.getLogger( name, AvalonLogger.AVALON_FQCN );
+ }
PaxLogger logger = m_paxLogging.getLogger( newName, AvalonLogger.AVALON_FQCN );
AvalonLogger avalonLogger = new AvalonLogger( logger );
- synchronized (m_loggers) {
- if (!m_loggers.containsKey(newName)) {
- m_loggers.put(newName, new LinkedList<AvalonLogger>());
+ if (m_paxLogging == null) {
+ synchronized (m_loggers) {
+ if (!m_loggers.containsKey(newName)) {
+ m_loggers.put(newName, new LinkedList<AvalonLogger>());
+ }
+ m_loggers.get(newName).add(avalonLogger);
}
- m_loggers.get(newName).add(avalonLogger);
}
return avalonLogger;
}
diff --git a/pax-logging-api/src/main/java/org/ops4j/pax/logging/log4jv2/Log4jv2Logger.java b/pax-logging-api/src/main/java/org/ops4j/pax/logging/log4jv2/Log4jv2Logger.java
index 467f1706..3e2742d7 100644
--- a/pax-logging-api/src/main/java/org/ops4j/pax/logging/log4jv2/Log4jv2Logger.java
+++ b/pax-logging-api/src/main/java/org/ops4j/pax/logging/log4jv2/Log4jv2Logger.java
@@ -31,7 +31,7 @@ import org.ops4j.pax.logging.internal.FallbackLogFactory;
*/
public class Log4jv2Logger extends AbstractLogger {
- private static final String LOG4J_FQCN = Logger.class.getName();
+ static final String LOG4J_FQCN = Logger.class.getName();
private volatile PaxLogger delegate;
diff --git a/pax-logging-api/src/main/java/org/ops4j/pax/logging/log4jv2/Log4jv2LoggerContext.java b/pax-logging-api/src/main/java/org/ops4j/pax/logging/log4jv2/Log4jv2LoggerContext.java
index 2288425e..dc3df163 100644
--- a/pax-logging-api/src/main/java/org/ops4j/pax/logging/log4jv2/Log4jv2LoggerContext.java
+++ b/pax-logging-api/src/main/java/org/ops4j/pax/logging/log4jv2/Log4jv2LoggerContext.java
@@ -16,11 +16,12 @@
*/
package org.ops4j.pax.logging.log4jv2;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
import org.apache.logging.log4j.message.MessageFactory;
-import org.apache.logging.log4j.spi.AbstractLogger;
import org.apache.logging.log4j.spi.ExtendedLogger;
import org.apache.logging.log4j.spi.LoggerContext;
import org.ops4j.pax.logging.OSGIPaxLoggingManager;
@@ -32,17 +33,22 @@ import org.osgi.framework.BundleContext;
*/
public class Log4jv2LoggerContext implements LoggerContext {
- private static final ConcurrentMap<String, Log4jv2Logger> loggers = new ConcurrentHashMap<String, Log4jv2Logger>();
+ private static final Map<String, List<Log4jv2Logger>> m_loggers = new WeakHashMap<String, List<Log4jv2Logger>>();
static PaxLoggingManager paxLogging;
public static void setBundleContext( BundleContext ctx )
{
- paxLogging = new OSGIPaxLoggingManager( ctx );
- for (Log4jv2Logger logger : loggers.values()) {
- logger.setPaxLoggingManager(paxLogging);
+ synchronized (m_loggers) {
+ paxLogging = new OSGIPaxLoggingManager( ctx );
+ for (List<Log4jv2Logger> loggers : m_loggers.values()) {
+ for (Log4jv2Logger logger : loggers) {
+ logger.setPaxLoggingManager(paxLogging);
+ }
+ }
+ paxLogging.open();
+ m_loggers.clear();
}
- paxLogging.open();
}
public static void dispose() {
@@ -59,18 +65,25 @@ public class Log4jv2LoggerContext implements LoggerContext {
@Override
public ExtendedLogger getLogger(final String name, final MessageFactory messageFactory) {
- final ExtendedLogger extendedLogger = loggers.get(name);
- if (extendedLogger != null) {
- AbstractLogger.checkMessageFactory(extendedLogger, messageFactory);
- return extendedLogger;
+ Log4jv2Logger log4jv2Logger = new Log4jv2Logger(name, messageFactory, paxLogging);
+ if (paxLogging == null) {
+ // just add the logger which PaxLoggingManager need to be replaced.
+ synchronized (m_loggers) {
+ if (!m_loggers.containsKey(name)) {
+ m_loggers.put(name, new LinkedList<Log4jv2Logger>());
+ }
+ m_loggers.get(name).add(log4jv2Logger);
+ }
}
- loggers.putIfAbsent(name, new Log4jv2Logger(name, messageFactory, paxLogging));
- return loggers.get(name);
+ return log4jv2Logger;
}
@Override
public boolean hasLogger(final String name) {
- return loggers.containsKey(name);
+ // we don't know in pax-logging... Because org.apache.logging.log4j.spi.LoggerContext.hasLogger()
+ // API may actually be bridged to Logback or Log4j1 or no backend at all.
+ // also, same "name" may be related to different loggers (associated with different bundles)
+ throw new UnsupportedOperationException("Operation not supported in pax-logging");
}
@Override
diff --git a/pax-logging-api/src/main/java/org/ops4j/pax/logging/slf4j/Slf4jLoggerFactory.java b/pax-logging-api/src/main/java/org/ops4j/pax/logging/slf4j/Slf4jLoggerFactory.java
index d2b123f5..f97d120d 100644
--- a/pax-logging-api/src/main/java/org/ops4j/pax/logging/slf4j/Slf4jLoggerFactory.java
+++ b/pax-logging-api/src/main/java/org/ops4j/pax/logging/slf4j/Slf4jLoggerFactory.java
@@ -58,6 +58,7 @@ public class Slf4jLoggerFactory
}
}
m_paxLogging.open();
+ m_loggers.clear();
}
}