几天前发生了有趣的问题。我们找到了解决方法,但原因尚不清楚。
我们有内部分布式日志跟踪器,它允许我们从分布式系统实时收集日志。 Tracer实现为logback扩展。在部署新版本时,我们抓住了ClassCircularityError
:
java.lang.ClassCircularityError: ch/qos/logback/core/spi/FilterReply
at me.bazhenov.whisperer.ActivatingTurboFilter.decide(ActivatingTurboFilter.java:33)
at ch.qos.logback.classic.spi.TurboFilterList.getTurboFilterChainDecision(TurboFilterList.java:62)
at ch.qos.logback.classic.LoggerContext.getTurboFilterChainDecision_0_3OrMore(LoggerContext.java:252)
这很有意思,因为FilterReply
是枚举,除了java.util.Enum
之外没有任何父母。所以,我认为ClassCircularityError
没有任何可能性。
在尝试调试此错误时,我们在开发环境中使用不同版本的Jetty运行Web应用程序。令人惊讶的是,得到了(注意行标记为*
):
Exception in thread "Scanner-0" java.lang.StackOverflowError
at java.io.UnixFileSystem.getBooleanAttributes0(Native Method)
at java.io.UnixFileSystem.getBooleanAttributes(UnixFileSystem.java:242)
at java.io.File.exists(File.java:819)
at sun.misc.URLClassPath$FileLoader.getResource(URLClassPath.java:1245)
at sun.misc.URLClassPath.getResource(URLClassPath.java:212)
at java.net.URLClassLoader$1.run(URLClassLoader.java:365)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at org.eclipse.jetty.webapp.WebAppClassLoader.findClass(WebAppClassLoader.java:524)
at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:457)
at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:417)
* at me.bazhenov.whisperer.ActivatingTurboFilter.decide(ActivatingTurboFilter.java:32)
at ch.qos.logback.classic.spi.TurboFilterList.getTurboFilterChainDecision(TurboFilterList.java:62)
at ch.qos.logback.classic.LoggerContext.getTurboFilterChainDecision_0_3OrMore(LoggerContext.java:252)
at ch.qos.logback.classic.Logger.callTurboFilters(Logger.java:772)
at ch.qos.logback.classic.Logger.isDebugEnabled(Logger.java:490)
at ch.qos.logback.classic.Logger.isDebugEnabled(Logger.java:486)
at org.eclipse.jetty.util.log.JettyAwareLogger.isDebugEnabled(JettyAwareLogger.java:170)
at org.eclipse.jetty.util.log.Slf4jLog.isDebugEnabled(Slf4jLog.java:110)
at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:474)
at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:417)
* at me.bazhenov.whisperer.ActivatingTurboFilter.decide(ActivatingTurboFilter.java:32)
at ch.qos.logback.classic.spi.TurboFilterList.getTurboFilterChainDecision(TurboFilterList.java:62)
at ch.qos.logback.classic.LoggerContext.getTurboFilterChainDecision_0_3OrMore(LoggerContext.java:252)
at ch.qos.logback.classic.Logger.callTurboFilters(Logger.java:772)
at ch.qos.logback.classic.Logger.isDebugEnabled(Logger.java:490)
at ch.qos.logback.classic.Logger.isDebugEnabled(Logger.java:486)
at org.eclipse.jetty.util.log.JettyAwareLogger.isDebugEnabled(JettyAwareLogger.java:170)
at org.eclipse.jetty.util.log.Slf4jLog.isDebugEnabled(Slf4jLog.java:110)
at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:474)
at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:417)
* at me.bazhenov.whisperer.ActivatingTurboFilter.decide(ActivatingTurboFilter.java:32)
at ch.qos.logback.classic.spi.TurboFilterList.getTurboFilterChainDecision(TurboFilterList.java:62)
at ch.qos.logback.classic.LoggerContext.getTurboFilterChainDecision_0_3OrMore(LoggerContext.java:252)
at ch.qos.logback.classic.Logger.callTurboFilters(Logger.java:772)
at ch.qos.logback.classic.Logger.isDebugEnabled(Logger.java:490)
at ch.qos.logback.classic.Logger.isDebugEnabled(Logger.java:486)
[...]
at org.eclipse.jetty.util.log.JettyAwareLogger.isDebugEnabled(JettyAwareLogger.java:170)
at org.eclipse.jetty.util.log.Slf4jLog.isDebugEnabled(Slf4jLog.java:110)
at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:474)
at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:417)
ActivatingTurboFilter.java:32
包含以下代码:
String value = MDC.get(key)
这似乎是递归类加载问题。其中一个类加载器进行日志记录,因此控制流程会在ActivatingTurboFilter
中返回,因为我们使用自定义过滤器和附加程序扩展了Logback。但是仍然没有加载所需的类,所以循环关闭。
我们可以通过在ActivatingTurboFilter
中添加静态初始值设定项来解决此问题:
static {
MDC.get("foo")
}
这有效地加载了加载ActivatingTurboFilter
本身的所有必需类,而不是调用ActivatingTurboFilter
方法的时刻。
但问题仍然存在。 Java是否允许这种类型的“递归类加载”,如果是这样,为什么这个问题已经表现出来?