Tomcat 记录混乱

Tomcat logging confusion

我全新安装了 tomcat。

为了改进日志记录,我编辑了 conf/logging.properties.

步骤 1

我换行

java.util.logging.ConsoleHandler.level = FINE

java.util.logging.ConsoleHandler.level = FINER

第 2 步

并附加行

org.apache.catalina.level = FINER

步骤 3

然后我启动服务器,如果我无法使用用户名 MyUsernameMyPasswordhttp://localhost:8080/manager/ 进行身份验证,我会看到此输出:

FINE [http-bio-443-exec-2] org.apache.catalina.authenticator.AuthenticatorBase.invoke Security checking request GET /manager/html
FINE [http-bio-443-exec-2] org.apache.catalina.loader.WebappClassLoaderBase.loadClass loadClass(org.apache.catalina.manager.HTMLManagerServlet, false)
FINE [http-bio-443-exec-2] org.apache.catalina.loader.WebappClassLoaderBase.loadClass   Delegating to parent classloader1 java.net.URLClassLoader@1218025c
FINE [http-bio-443-exec-2] org.apache.catalina.loader.WebappClassLoaderBase.loadClass   Loading class from parent
FINE [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.findSecurityConstraints   Checking constraint 'SecurityConstraint[Status interface]' against GET /html --> false
FINE [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.findSecurityConstraints   Checking constraint 'SecurityConstraint[JMX Proxy interface]' against GET /html --> false
FINE [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.findSecurityConstraints   Checking constraint 'SecurityConstraint[Text Manager interface (for scripts)]' against GET /html --> false
FINE [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.findSecurityConstraints   Checking constraint 'SecurityConstraint[HTML Manager interface (for humans)]' against GET /html --> true
FINE [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.findSecurityConstraints   Checking constraint 'SecurityConstraint[Status interface]' against GET /html --> false
FINE [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.findSecurityConstraints   Checking constraint 'SecurityConstraint[JMX Proxy interface]' against GET /html --> false
FINE [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.findSecurityConstraints   Checking constraint 'SecurityConstraint[Text Manager interface (for scripts)]' against GET /html --> false
FINE [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.findSecurityConstraints   Checking constraint 'SecurityConstraint[HTML Manager interface (for humans)]' against GET /html --> true
FINE [http-bio-443-exec-2] org.apache.catalina.authenticator.AuthenticatorBase.invoke  Calling hasUserDataPermission()
FINE [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.hasUserDataPermission   User data constraint has no restrictions
FINE [http-bio-443-exec-2] org.apache.catalina.authenticator.AuthenticatorBase.invoke  Calling authenticate()
FINER [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.authenticate Username MyUsername NOT successfully authenticated
FINE [http-bio-443-exec-2] org.apache.catalina.authenticator.AuthenticatorBase.invoke  Failed authenticate() test
FINE [http-bio-443-exec-2] org.apache.catalina.core.StandardHostValve.custom Processing ErrorPage[errorCode=401, location=/WEB-INF/jsp/401.jsp]
FINER [http-bio-443-exec-2] org.apache.catalina.core.StandardWrapper.allocate   Returning non-STM instance
FINE [http-bio-443-exec-2] org.apache.catalina.core.ApplicationDispatcher.doForward  Disabling the response for futher output
FINE [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.session.ManagerBase.processExpires Start expire sessions StandardManager at 1525428004090 sessioncount 0
FINE [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.session.ManagerBase.processExpires End expire sessions StandardManager processingTime 4 expired sessions: 0

问题

现在我修改步骤2并将包realm添加到定义中。 现在第 2 步添加了这一行:

org.apache.catalina.realm.level = FINER

为什么 FINER-Loggings 消失了?我的意思是,org.apache.catalina.realm 更具体,对吧?

如果我的理解正确,您最终会得到一个如下所示的日志记录属性:

java.util.logging.ConsoleHandler.level = FINER
org.apache.catalina.realm.level = FINER

Java Logging Overview section 1.1 状态:

Applications make logging calls on Logger objects. Loggers are organized in a hierarchical namespace and child Loggers may inherit some logging properties from their parents in the namespace.

读取记录器名称时,父记录器位于点的左侧。 因此,org.apache.catalinaorg.apache.catalina.realmorg.apache.catalina.core 的父级。

无论如何,执行代码代码必须demand a logger in order for it to exist. Simply adding lines in the properties file doesn't create loggers. If it did they would just be garbage collected。这意味着假设您有一个记录器树 A <- B <- C。您想要为 B 以及所有 B 子级设置级别,因此您在属性文件中添加 A.B.level 行。但是,在 运行 时,需要的记录器是 AC

因此,当您期望 "" <- A <- A.B <- A.B.C

时,您必须使用 "" <- A <- A.B.C

综上所述,我认为在 运行 时您的记录器看起来像:

"" <- org.apache.catalina <- org.apache.catalina.realm.RealmBase 并且没有 Tomcat 代码创建了一个名为 org.apache.catalina.realm.

的实际记录器

您可以通过将 JConsole 连接到 运行 安装 JVM 并检查 MBean 选项卡并列出记录器名称来进行验证。在 Tomcat 这将不起作用,因为返回的记录器取决于调用类加载器。

要解决此问题,您可以使用 config option to demand and hold the logger in memory. You just have to install this on the custom code on the system class path

否则您必须在属性文件中指定所有已知的子记录器名称,这可能会很冗长。

First idea: If we look at the CSS specifity we know that .mydiv{ color:red} is not as specific as div.mydiv{color: green} so a div.mydiv contains text in red? Inheriance means if not specified what is anti-inheritance.

与 CSS 不同,JUL 有一个 运行 时间记录器树和一个属性文件。 运行时间记录器树随着执行代码需要记录器而动态调整。只有在代码需要记录器时才会使用这些属性。在属性文件中指定记录器名称并不意味着什么,除非记录器是由代码创建的。这可以更改记录器的父级,从而更改级别。试一试:

package so;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

public class TheMissingParent {

    private static final Logger[] LAZY_ONE;
    static {
        LAZY_ONE = new Logger[] { Logger.getLogger("org.apache.catalina"),
                Logger.getLogger("org.apache.catalina.realm.RealmBase") };
    }

    private static volatile Logger[] LAZY_TWO;

    public static void main(String[] args) {
        loadProperties();
        printAncestors(LAZY_ONE);
        findlLongLostParents();
        System.out.println("====");
        printAncestors(LAZY_ONE);
    }

    private static void loadProperties() {
        Properties props = new Properties();
        props.put("org.apache.catalina.realm.level", "FINER");

        try(ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            props.store(out, "");
            LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(out.toByteArray()));
        } catch (IOException ioe) {
            throw new AssertionError(ioe);
        }
    }

    private static void findlLongLostParents() {
        LAZY_TWO = new Logger[] {Logger.getLogger("org.apache.catalina.realm") };
    }

    private static void printAncestors(Logger[] loggers) {
        // System.out.println(loggers.toString());
        for (Logger l : loggers) {
            printAncestors(l);
            System.out.println();
        }
    }

    private static void printAncestors(Logger l) {
        if (l != null) {
            printAncestors(l.getParent());
            System.out.print("<-");
            String name = l.getName();
            if (name != null && name.isEmpty()) {
                System.out.append("\"\"");
            } else {
                System.out.append(name);
            }

            for(Logger p = l; p != null; p = p.getParent()) {
                Level lvl = p.getLevel();
                if (lvl != null) {
                    System.out.append('{').append(lvl.getName()).append('}');
                    break;
                }
            }
        }
    }
}

这将输出:

<-""{INFO}<-org.apache.catalina{INFO}
<-""{INFO}<-org.apache.catalina{INFO}<-org.apache.catalina.realm.RealmBase{INFO}
====
<-""{INFO}<-org.apache.catalina{INFO}
<-""{INFO}<-org.apache.catalina{INFO}<-org.apache.catalina.realm{FINER}<-org.apache.catalina.realm.RealmBase{FINER}

这是核心问题。如果 Tomcat(或某些自定义代码)从不要求 org.apache.catalina.realm 记录器,则属性文件中的那一行只是死代码。

Second, if you say so, noone specified to org.apache.catalina nor org.apache nor org nor `` to be level INFO, so where would the value INFO came from?

该行为描述为 in the LoggerManager class level docs:

If neither of these properties is defined then, as described above, the LogManager will read its initial configuration from a properties file "lib/logging.properties" in the JRE directory.

名为空字符串的根记录器 "" 是所有记录器的父级。始终创建根记录器。