Bug 66406 - JULI ClassLoaderLogManager creates multiple loggers named ""
Summary: JULI ClassLoaderLogManager creates multiple loggers named ""
Status: NEW
Alias: None
Product: Tomcat 10
Classification: Unclassified
Component: Catalina (show other bugs)
Version: 10.1.4
Hardware: PC All
: P2 enhancement (vote)
Target Milestone: ------
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-01-02 11:18 UTC by Matthew Firth
Modified: 2023-01-06 12:32 UTC (History)
0 users



Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Matthew Firth 2023-01-02 11:18:00 UTC
When enabling ClassLoaderLogManager, the top of the logger are three instances of ClassLoaderLogManager$RootLogger - all named "".

Whilst I can't point to anything in the spec to say that Loggers must be uniquely named, the implementation of java.util.LogManager's getLogger() (via LoggerContext.findLogger()) itself uses a HashMap to lookup Logger instances by name.  That might suggest that ClassLoaderLogManager's behaviour is, at minimum, unexpected.

I found this situation when attempting to use Log4J's Log4jBridgeHandler and found my logs were getting published twice:  the duplicate Loggers foiled Log4jBridgeHandler's logic in trying to remove existing handlers on the JULI Root Logger.
Comment 1 Piotr P. Karwasz 2023-01-03 08:42:51 UTC
I believe that this behavior is dictated by the lack of support in JUL for logger contexts, which forces Tomcat to use several hacks and tricks.

However, I have to partially agree with this report. The way Tomcat JULI deals with `Handler` sharing between applications is inconsistent. If an application does not have a `logging.properties` file (very common case):

 * the root logger of an application has the global root logger as parent and `useParentHandlers` set to true. This way it delegates all logging to the global root logger handlers,
 * all other loggers copy the references to global handlers as their own.

I believe that this architecture can be simplified and remove the need for a parent of the root logger. I'll try to submit a PR for it.
Comment 2 Mark Thomas 2023-01-05 17:52:09 UTC
Tomcat JULI is explicitly intended to permit multiple loggers with the same name.

Consider the case where different applications using the same library are deployed to a Tomcat instance. If that library uses java.util logging, the applications will share loggers making it difficult/impossible to identify which messages originate from which application.

JULI solves this problem by extending the unique key for a logger from name to class loader + name. Since each web application has a dedicated class loader, loggers for each web application remain separate - even if both applications have a logger with the same name.

The description in comment #1 of how the handlers are configured does not match what I see with the current code.

Given that there is one root logger (called "") per class loader and that the requirement is for logging to be delegated to the parent class loader's handlers if the current class loader doesn't have an explicit configuration, I don't see how the current setup could be simplified but I'll leave this issue open for now  as it is always possible there is a solution I haven't thought of.

I am changing this to an enhancement request though as having multiple loggers with the same name, including the root loggers, is by design.
Comment 3 Piotr P. Karwasz 2023-01-06 12:32:41 UTC
Hi Mark,

(In reply to Mark Thomas from comment #2)
> The description in comment #1 of how the handlers are configured does not
> match what I see with the current code.

I was referring to this snippet of code, that deals with the case, where the application's classloader does not contain a `logging.properties` resource:

        if (is == null) {
            // Retrieve the root logger of the parent classloader instead
            ClassLoader current = classLoader.getParent();
            ClassLoaderLogInfo info = null;
            while (current != null && info == null) {
                info = getClassLoaderInfo(current);
                current = current.getParent();
            }
            if (info != null) {
                localRootLogger.setParent(info.rootNode.logger);
            }
        }

https://github.com/apache/tomcat/blob/bb81a2cf7abf2384e73bdac0313731b8963b9b66/java/org/apache/juli/ClassLoaderLogManager.java#L496

I believe that the reporter's main problem isn't having multiple loggers with the same name, but having a root logger that has a parent. That is the reason the code that removes all root handlers and replaces them with a Log4jBridgeHandler does not work: https://issues.apache.org/jira/browse/LOG4J2-3646

We can probably greatly reduce the `if` clause above, by ensuring that a `logging.properties` file is always found.

There are however implications to such a change: Spring Boot uses heuristic to decide whether or not install a Slf4jBridgeHandler. IIRC the current version checks if the root logger has a single Console handler. In the current Tomcat JULI version the root logger of a web app has no handlers, if we make the change above, it will have all the handlers from the standard Catalina `logging.properties`.

https://github.com/spring-projects/spring-boot/issues/8933