我正在尝试关注SBT 0.12.1项目中的log4j2 configuration tutorials。这是我的 build.sbt :
name := "Logging Test"
version := "0.0"
scalaVersion := "2.9.2"
libraryDependencies ++= Seq(
"org.apache.logging.log4j" % "log4j-api" % "2.0-beta3",
"org.apache.logging.log4j" % "log4j-core" % "2.0-beta3"
)
我有两个独立的主要课程。第一个是 src / main / scala / logtest / ScalaTest.scala 中的logtest.ScalaTest
:
package logtest
import org.apache.logging.log4j.{Logger, LogManager}
object ScalaTest {
private val logger = LogManager.getLogger(getClass())
def main(args: Array[String]) {
logger.trace("Entering application.")
val bar = new Bar()
if (!bar.doIt())
logger.error("Didn't do it.")
logger.trace("Exiting application.")
}
}
,第二个是 src / main / java / logtest / JavaTest.java 中的logtest.JavaTest
:
package logtest;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
public class JavaTest {
private static Logger logger = LogManager.getLogger(JavaTest.class.getName());
public static void main(String[] args) {
logger.trace("Entering application.");
Bar bar = new Bar();
if (!bar.doIt())
logger.error("Didn't do it.");
logger.trace("Exiting application.");
}
}
如果我从sbt内部运行logtest.ScalaTest.main()
,我得到了我期望的输出,因为 src / main / resources / log4j2.xml 将根日志记录级别设置为跟踪:
> run-main logtest.ScalaTest
[info] Running logtest.ScalaTest
10:26:23.730 [run-main] TRACE logtest.ScalaTest$ - Entering application.
10:26:23.733 [run-main] TRACE logtest.Bar - entry
10:26:23.733 [run-main] ERROR logtest.Bar - Did it again!
10:26:23.733 [run-main] TRACE logtest.Bar - exit with (false)
10:26:23.733 [run-main] ERROR logtest.ScalaTest$ - Didn't do it.
10:26:23.733 [run-main] TRACE logtest.ScalaTest$ - Exiting application.
[success] Total time: 0 s, completed Dec 21, 2012 10:26:23 AM
然而,当我从sbt内部运行logtest.JavaTest.main()
时,我得到了不同的输出
> run-main logtest.JavaTest
[info] Running logtest.JavaTest
ERROR StatusLogger Unable to locate a logging implementation, using SimpleLogger
ERROR Bar Did it again!
ERROR JavaTest Didn't do it.
[success] Total time: 0 s, completed Dec 21, 2012 10:27:29 AM
据我所知,ERROR StatusLogger Unable to ...
通常表示log4j-core不在我的类路径中。缺少TRACE消息似乎表明我的log4j2.xml设置也不在类路径上。如果我运行Foo.main与LoggerTest.main,为什么类路径会有任何差异?或者是否有其他原因造成这种行为?
我使用SBT Assembly构建了这个项目的胖罐,并指定logtest.JavaTest作为主类。从命令行运行它会产生正确的结果:
$ java -jar "Logging Test-assembly-0.0.jar"
10:29:41.089 [main] TRACE logtest.JavaTest - Entering application.
10:29:41.091 [main] TRACE logtest.Bar - entry
10:29:41.091 [main] ERROR logtest.Bar - Did it again!
10:29:41.091 [main] TRACE logtest.Bar - exit with (false)
10:29:41.091 [main] ERROR logtest.JavaTest - Didn't do it.
10:29:41.091 [main] TRACE logtest.JavaTest - Exiting application.
按照Edmondo1984的建议,我整理了一个完整的例子并将其放在github上。
答案 0 :(得分:1)
这类问题通常是由于类加载差异造成的,在这种情况下差异非常小。
在此初始化阶段,首次加载类时将调用LogManager
静态初始值设定项。如果您查看静态初始化程序,您将看到:
Enumeration<URL> enumResources = null;
try {
enumResources = cl.getResources(LOGGER_RESOURCE);
} catch (IOException e) {
logger.fatal("Unable to locate " + LOGGER_RESOURCE, e);
}
稍后在代码中,您将看到枚举资源的循环以创建记录器上下文工厂。
但是,当您运行Scala类时enumResources.hasMoreElements()
返回 true ,而当您运行java类时,它会返回 false (因此没有记录器上下文而没有记录器完全添加到LogManager中。
如果进一步调查,您会发现cl
变量实际上是一个类加载器,如果Java类是sun.misc.Launcher$AppClassLoader
的实例,而Scala类是一个实例sbt.classpath.ClasspathUtilities$$anon$1
如果查看静态初始值设定项的开头,您将看到以下语句:
static {
// Shortcut binding to force a specific logging implementation.
PropsUtil managerProps = new PropsUtil("log4j2.LogManager.properties");
String factoryClass = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
ClassLoader cl = findClassLoader();
所以你可能想看一下findClassLoader()
方法:
private static ClassLoader findClassLoader() {
ClassLoader cl;
if (System.getSecurityManager() == null) {
cl = Thread.currentThread().getContextClassLoader();
} else {
cl = java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<ClassLoader>() {
public ClassLoader run() {
return Thread.currentThread().getContextClassLoader();
}
}
);
}
if (cl == null) {
cl = LogManager.class.getClassLoader();
}
return cl;
}
在这两种情况下,由于SecurityManager不为null,因此它返回当前的Thread上下文类加载器。这与您的Java类和Scala类不同。