Tomcat日志混淆 [英] Tomcat logging confusion

查看:31
本文介绍了Tomcat日志混淆的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我全新安装了 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 安全检查请求 GET/manager/htmlFINE [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 委托给父类加载器1 java.net.URLClassLoader@1218025cFINE [http-bio-443-exec-2] org.apache.catalina.loader.WebappClassLoaderBase.loadClass 从父级加载类FINE [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.findSecurityConstraints 检查约束 'SecurityConstraint[Status interface]' 对 GET/html -->错误的FINE [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.findSecurityConstraints 检查约束SecurityConstraint[JMX 代理接口]"对 GET/html -->错误的FINE [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.findSecurityConstraints 检查约束 'SecurityConstraint[Text Manager interface (for scripts)]' 对 GET/html -->错误的FINE [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.findSecurityConstraints 检查约束SecurityConstraint[HTML 管理器界面(用于人类)]"对 GET/html -->真的FINE [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.findSecurityConstraints 检查约束 'SecurityConstraint[Status interface]' 对 GET/html -->错误的FINE [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.findSecurityConstraints 检查约束SecurityConstraint[JMX 代理接口]"对 GET/html -->错误的FINE [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.findSecurityConstraints 检查约束 'SecurityConstraint[Text Manager interface (for scripts)]' 对 GET/html -->错误的FINE [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.findSecurityConstraints 检查约束SecurityConstraint[HTML 管理器界面(用于人类)]"对 GET/html -->真的FINE [http-bio-443-exec-2] org.apache.catalina.authenticator.AuthenticatorBase.invoke 调用 hasUserDataPermission()FINE [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.hasUserDataPermission 用户数据约束没有限制FINE [http-bio-443-exec-2] org.apache.catalina.authenticator.AuthenticatorBase.invoke 调用authenticate()FINER [http-bio-443-exec-2] org.apache.catalina.realm.RealmBase.authenticate 用户名 MyUsername 未成功通过身份验证FINE [http-bio-443-exec-2] org.apache.catalina.authenticator.AuthenticatorBase.invoke 失败的身份验证()测试FINE [http-bio-443-exec-2] org.apache.catalina.core.StandardHostValve.custom 处理 ErrorPage[errorCode=401, location=/WEB-INF/jsp/401.jsp]FINER [http-bio-443-exec-2] org.apache.catalina.core.StandardWrapper.allocate 返回非 STM 实例FINE [http-bio-443-exec-2] org.apache.catalina.core.ApplicationDispatcher.doForward 禁用对进一步输出的响应FINE [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.session.ManagerBase.processExpires 在 1525428004090 sessioncount 0 开始过期会话 StandardManagerFINE [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.session.ManagerBase.processExpires 结束过期会话 StandardManager processingTime 4 过期会话:0

问题

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

org.apache.catalina.realm.level = FINER

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

解决方案

如果我没有理解错,你最终会得到一个类似于以下的日志属性:

java.util.logging.ConsoleHandler.level = FINERorg.apache.catalina.realm.level = 更好

Java 日志记录概述部分1.1 声明:

<块引用>

应用程序对 Logger 对象进行日志记录调用.Logger 被组织在一个分层的命名空间中,子 Logger 可以从命名空间中的父级继承一些日志属性.

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

执行代码代码必须需要一个记录器才能让它存在.简单地在属性文件中添加行不会创建记录器.如果这样做,它们只会是 垃圾收集.这意味着假设您有一个记录器树 A <- B <- C.您想为 B 以及所有 B 子级设置级别,因此您在属性文件中添加 A.B.level 行.但是,在运行时需要的记录器是 AC

所以你必须使用的是 "" <- A <- ABC 当你期待 "" <- A <- AB <- ABC

综合起来,我认为在运行时你的记录器看起来像:

"" <- org.apache.catalina <- org.apache.catalina.realm.RealmBase 并且没有 Tomcat 代码创建了一个名为 org.apache.catalina 的实际记录器.realm.

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

要解决此问题,您可以使用 config 选项来要求并将记录器保存在内存中.您只需要在 系统类路径.

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

<块引用>

第一个想法:如果我们查看 CSS 规范,我们知道 .mydiv{color:red} 不像 div.mydiv{color: green} 那样具体,所以 div.mydiv 包含红色文本?继承是指如果不指定什么是反继承.

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

打包so;导入 java.io.ByteArrayInputStream;导入 java.io.ByteArrayOutputStream;导入 java.io.IOException;导入 java.util.Properties;导入 java.util.logging.Level;导入 java.util.logging.LogManager;导入 java.util.logging.Logger;公共类 TheMissingParent {私有静态最终记录器[] LAZY_ONE;静止的 {LAZY_ONE = new Logger[] { Logger.getLogger("org.apache.catalina"),Logger.getLogger("org.apache.catalina.realm.RealmBase") };}私有静态易失记录器 [] LAZY_TWO;公共静态无效主(字符串 [] args){加载属性();打印祖先(LAZY_ONE);findlLongLostParents();System.out.println("====");打印祖先(LAZY_ONE);}私有静态无效负载属性(){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()));} 捕捉(IOException ioe){抛出新的断言错误(ioe);}}私有静态无效 findlLongLostParents() {LAZY_TWO = new Logger[] {Logger.getLogger("org.apache.catalina.realm") };}私有静态无效printAncestors(记录器[]记录器){//System.out.println(loggers.toString());for (记录器 l : 记录器) {打印祖先(l);System.out.println();}}私有静态无效printAncestors(记录器l){如果 (l != null) {打印祖先(l.getParent());System.out.print("<-");字符串名称 = l.getName();if (name != null && name.isEmpty()) {System.out.append("\"\"");} 别的 {System.out.append(name);}for(Logger p = l; p != null; p = p.getParent()) {等级 lvl = p.getLevel();如果(lvl != null){System.out.append('{').append(lvl.getName()).append('}');休息;}}}}}

这将输出:

<-""{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 记录器,那么属性文件中的那一行就是死代码.

<块引用>

第二,如果你这么说,没有人指定 org.apache.catalina 或 org.apache 或 org 或 `` 为级别 INFO,那么 INFO 的值从何而来?

在 LoggerManager 中描述了这种行为班级文档:

<块引用>

如果这些属性均未定义,则如上所述,LogManager 将从 JRE 目录中的属性文件lib/logging.properties"中读取其初始配置.

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

I have a fresh installaion of tomcat.

To improove the logging I edit the conf/logging.properties.

Step 1

I change the Line

java.util.logging.ConsoleHandler.level = FINE

to

java.util.logging.ConsoleHandler.level = FINER

Step 2

And append the line

org.apache.catalina.level = FINER

Step 3

Then I start the server and if I fail to authenticate to http://localhost:8080/manager/ with username MyUsername and MyPassword I see this output:

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

Question

Now I modify Step 2 and add the package realm to the definition. Now the Step 2 adds this line instead:

org.apache.catalina.realm.level = FINER

Why do the FINER-Loggings disappear? I mean, org.apache.catalina.realm is more specific, right?

解决方案

If I'm understanding correctly you end up with a logging properties that looks like:

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

The Java Logging Overview section 1.1 states:

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.

When reading the logger names the parent loggers are to the left of the dots. Therefore, org.apache.catalina is the parent of org.apache.catalina.realm and org.apache.catalina.core.

The executing code code has to 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 anyway. This means that say you have a logger tree A <- B <- C. You want to set the level for B and therefor all of B children so you add A.B.level line in the properties file. However, at run-time the loggers that are demanded are A and C

So what you have to work with is "" <- A <- A.B.C when you are expecting "" <- A <- A.B <- A.B.C

Putting that all together, I think at runtime your loggers look like:

"" <- org.apache.catalina <- org.apache.catalina.realm.RealmBase and no Tomcat code has created an actual logger named org.apache.catalina.realm.

You can verify by connecting JConsole to runnting JVM and checking the MBean tab and listing the logger names. On Tomcat this won't work because the loggers returned depend on the calling classloader.

To work around this you can use the 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.

Otherwise you have to specify all the known child logger names in the properties file which can be verbose.

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.

Unlike CSS, JUL has a runtime logger tree and a properties file. That runtime logger tree is dynamically adjusted as executing code demands loggers. The properties are only ever used if code demands a logger. Specifying a logger name in the properties file doesn't mean a thing unless the logger is created by code. This can change the parents of a logger and therefore the level. Give it a spin:

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;
                }
            }
        }
    }
}

This will output:

<-""{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}

This is the core issue. If Tomcat (or some custom code) never demands a org.apache.catalina.realm logger that line in the properties file is just dead code.

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?

That behavior is described 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.

The root logger named as empty string "" is the parent of all loggers. The root logger is always created.

这篇关于Tomcat日志混淆的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆