草庐IT

java - SBT 运行 scala 和 java 之间的差异?

coder 2024-03-08 原文

我正在尝试关注 log4j2 configuration tutorials在 SBT 0.12.1 项目中。这是我的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 构建了这个项目的 fat 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 上。 .

最佳答案

这类问题通常是由于类加载差异造成的,在这种情况下,差异是非常重要的。

在此初始化阶段,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 不为空,它返回当前线程上下文类加载器。这对于您的 Java 类和 Scala 类是不同的。

关于java - SBT 运行 scala 和 java 之间的差异?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13955166/

有关java - SBT 运行 scala 和 java 之间的差异?的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  3. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  4. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  5. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  6. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  7. ruby - Sinatra:运行 rspec 测试时记录噪音 - 2

    Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/

  8. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  9. ruby - #之间? Cooper 的 *Beginning Ruby* 中的错误或异常 - 2

    在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee

  10. ruby-on-rails - `a ||= b` 和 `a = b if a.nil 之间的区别? - 2

    我正在检查一个Rails项目。在ERubyHTML模板页面上,我看到了这样几行:我不明白为什么不这样写:在这种情况下,||=和ifnil?有什么区别? 最佳答案 在这种特殊情况下没有区别,但可能是出于习惯。每当我看到nil?被使用时,它几乎总是使用不当。在Ruby中,很少有东西在逻辑上是假的,只有文字false和nil是。这意味着像if(!x.nil?)这样的代码几乎总是更好地表示为if(x)除非期望x可能是文字false。我会将其切换为||=false,因为它具有相同的结果,但这在很大程度上取决于偏好。唯一的缺点是赋值会在每次运行

随机推荐