9

SBT0.12.1プロジェクトのlog4j2構成チュートリアルに従おうとしています。これが私の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"
)

私は2つの別々のメインクラスを持っています。logtest.ScalaTest1つ目はsrc/main / scala / logtest / ScalaTest.scalaにあります:

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.")
  }
}

2番目はlogtest.JavaTestsrc /main / java / logtest / JavaTest.javaにあります:

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.");
  }
}

logtest.ScalaTest.main() sbt内から実行すると、 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

ただし、 logtest.JavaTest.main()sbt内から実行すると、異なる出力が得られます

> 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アセンブリを使用してこのプロジェクトのファットjarを構築し、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.

GitHubの例

Edmondo1984の提案に従って、完全な例をまとめてgithubに掲載しました。

4

1 に答える 1

1

この種の問題は、クラスの読み込みの違いが原因であることが非常に多く、この場合、違いは些細なことではありません。

この初期化フェーズではLogManager、クラスが最初にロードされるときに静的初期化子が呼び出されます。静的初期化子の内部を見ると、次のように表示されます。

        Enumeration<URL> enumResources = null;
        try {
            enumResources = cl.getResources(LOGGER_RESOURCE);
        } catch (IOException e) {
            logger.fatal("Unable to locate " + LOGGER_RESOURCE, e);
        }

コードの後半で、列挙型リソースをループしてロガーコンテキストファクトリを作成します。

ただし、Scalaクラスを実行するとtrueenumResources.hasMoreElements()が返され、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ではないため、現在のスレッドコンテキストクラスローダーを返します。これは、JavaクラスとScalaクラスで異なります。

于 2012-12-28T09:27:23.290 に答える