代码之家  ›  专栏  ›  技术社区  ›  user4955663

如何在ScalaJS/PhantomJS中使用log4javascript

  •  0
  • user4955663  · 技术社区  · 9 年前

    我尝试用ScalaJS和PhantomJS运行log4javascript,并得到一个错误,用Rhino就可以了。

    我举了以下ScalaJS示例: https://github.com/scala-js/scalajs-tutorial

    和log4javascript,我取自另一个ScalaJS示例: https://github.com/ochrons/scalajs-spa-tutorial 更具体地说,这3个文件: https://github.com/ochrons/scalajs-spa-tutorial/tree/master/client/src/main/scala/spatutorial/client/logger

    我修改了TutorialApp以包括一些日志记录:

    package tutorial.webapp
    
    import scala.scalajs.js.JSApp
    
    import org.scalajs.jquery.jQuery
    import tutorial.logger.LoggerFactory
    
    object TutorialApp extends JSApp {
      println("Before getLogger...")
      val log = LoggerFactory.getLogger(getClass().getName)
      log.info("After getLogger...")
    
      def main(): Unit = {
        jQuery(setupUI _)
      }
    
      def setupUI(): Unit = {
        jQuery("""<button type="button">Click me!</button>""")
          .click(addClickedMessage _)
          .appendTo(jQuery("body"))
        jQuery("body").append("<p>Hello World</p>")
      }
    
      def addClickedMessage(): Unit = {
        jQuery("body").append("<p>You clicked the button!</p>")
        log.info("Button clicked...")
      }
    }
    

    修改的build.sbt

    enablePlugins(ScalaJSPlugin)
    
    name := "Scala.js Tutorial"
    
    scalaVersion := "2.11.7"
    
    libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "0.8.1"
    libraryDependencies += "be.doeraene" %%% "scalajs-jquery" % "0.8.0"
    
    jsDependencies += RuntimeDOM
    
    skip in packageJSDependencies := false
    
    // uTest settings
    libraryDependencies += "com.lihaoyi" %%% "utest" % "0.3.0" % "test"
    testFrameworks += new TestFramework("utest.runner.Framework")
    
    persistLauncher in Compile := true
    persistLauncher in Test := false
    
    // Modifications to original tutorial are here:
    scalaJSStage in Global := FastOptStage // If NOT commented out: Uses Phantom, if IS commented out: Uses Rhino
    
    libraryDependencies += "org.webjars" % "log4javascript" % "1.4.13"
    
    jsDependencies += "org.webjars" % "log4javascript" % "1.4.13" / "1.4.13/log4javascript.js" 
    

    Logger文件位于src/main/scala/tutorial/Logger中,只有包名被更改才能编译。 修改的package.scala:

    package tutorial
    
    package object logger {
      private val defaultLogger = LoggerFactory.getLogger("Log")
    
      def log = defaultLogger
    }
    

    修改的LoggerFactory.scala

    package tutorial.logger
    
    import scala.annotation.elidable
    import scala.annotation.elidable._
    
    trait Logger {
      /*
       * Use @elidable annotation to completely exclude functions from the compiler generated byte-code based on
       * the specified level. In a production build most logging functions will simply disappear with no runtime
       * performance penalty.
       *
       * Specify level as a compiler parameter
       * > scalac -Xelide-below INFO
      */
      @elidable(FINEST) def trace(msg: String, e: Exception): Unit
      @elidable(FINEST) def trace(msg: String): Unit
      @elidable(FINE) def debug(msg: String, e: Exception): Unit
      @elidable(FINE) def debug(msg: String): Unit
      @elidable(INFO) def info(msg: String, e: Exception): Unit
      @elidable(INFO) def info(msg: String): Unit
      @elidable(WARNING) def warn(msg: String, e: Exception): Unit
      @elidable(WARNING) def warn(msg: String): Unit
      @elidable(SEVERE) def error(msg: String, e: Exception): Unit
      @elidable(SEVERE) def error(msg: String): Unit
      @elidable(SEVERE) def fatal(msg: String, e: Exception): Unit
      @elidable(SEVERE) def fatal(msg: String): Unit
    
      def enableServerLogging(url: String): Unit
      def disableServerLogging(): Unit
    }
    
    object LoggerFactory {
      private[logger] def createLogger(name: String) = {}
    
      lazy val consoleAppender = new BrowserConsoleAppender
      lazy val popupAppender = new PopUpAppender
    
      /**
       * Create a logger that outputs to browser console
       */
      def getLogger(name: String): Logger = {
        val nativeLogger = Log4JavaScript.log4javascript.getLogger(name)
        nativeLogger.addAppender(consoleAppender)
        new L4JSLogger(nativeLogger)
      }
    
      /**
       * Create a logger that outputs to a separate popup window
       */
      def getPopUpLogger(name: String): Logger = {
        val nativeLogger = Log4JavaScript.log4javascript.getLogger(name)
        nativeLogger.addAppender(popupAppender)
        new L4JSLogger(nativeLogger)
      }
    }
    

    修改的Log4javascript.scala

    package tutorial.logger
    
    import scala.scalajs.js
    import scala.scalajs.js.annotation.JSName
    
    /**
     * Facade for functions in log4javascript that we need
     */
    @js.native
    private[logger] trait Log4JavaScript extends js.Object {
      def getLogger(name:js.UndefOr[String]):JSLogger = js.native
      def setEnabled(enabled:Boolean):Unit = js.native
      def isEnabled:Boolean = js.native
    }
    
    @js.native
    @JSName("log4javascript.Level")
    private[logger] trait Level extends js.Object {
      val ALL:Level = js.native
      val TRACE:Level = js.native
      val DEBUG:Level = js.native
      val INFO:Level = js.native
      val WARN:Level = js.native
      val ERROR:Level = js.native
      val FATAL:Level = js.native
    }
    
    @js.native
    @JSName("log4javascript.Logger")
    private[logger] trait JSLogger extends js.Object {
      def addAppender(appender:Appender):Unit = js.native
      def removeAppender(appender:Appender):Unit = js.native
      def removeAllAppenders(appender:Appender):Unit = js.native
      def setLevel(level:Level):Unit = js.native
      def getLevel:Level = js.native
      def trace(msg:String, error:js.UndefOr[js.Error]):Unit = js.native
      def debug(msg:String, error:js.UndefOr[js.Error]):Unit = js.native
      def info(msg:String, error:js.UndefOr[js.Error]):Unit = js.native
      def warn(msg:String, error:js.UndefOr[js.Error]):Unit = js.native
      def error(msg:String, error:js.UndefOr[js.Error]):Unit = js.native
      def fatal(msg:String, error:js.UndefOr[js.Error]):Unit = js.native
      def trace(msg:String):Unit = js.native
      def debug(msg:String):Unit = js.native
      def info(msg:String):Unit = js.native
      def warn(msg:String):Unit = js.native
      def error(msg:String):Unit = js.native
      def fatal(msg:String):Unit = js.native
    }
    
    @js.native
    @JSName("log4javascript.Layout")
    private[logger] trait Layout extends js.Object
    
    @js.native
    @JSName("log4javascript.JsonLayout")
    private[logger] class JsonLayout extends Layout
    
    @js.native
    @JSName("log4javascript.Appender")
    private[logger] trait Appender extends js.Object {
      def setLayout(layout:Layout):Unit = js.native
      def setThreshold(level:Level):Unit = js.native
    }
    
    @js.native
    @JSName("log4javascript.BrowserConsoleAppender")
    private[logger] class BrowserConsoleAppender extends Appender
    
    @js.native
    @JSName("log4javascript.PopUpAppender")
    private[logger] class PopUpAppender extends Appender
    
    @js.native
    @JSName("log4javascript.AjaxAppender")
    private[logger] class AjaxAppender(url:String) extends Appender {
      def addHeader(header:String, value:String):Unit = js.native
    }
    
    @js.native
    private[logger] object Log4JavaScript extends js.GlobalScope {
      val log4javascript:Log4JavaScript = js.native
    }
    
    class L4JSLogger(jsLogger:JSLogger) extends Logger {
    
      private var ajaxAppender:AjaxAppender = null
    
      private def undefOrError(e:Exception):js.UndefOr[js.Error] = {
        if(e == null)
          js.undefined
        else
          e.asInstanceOf[js.Error]
      }
    
      override def trace(msg: String, e: Exception): Unit = jsLogger.trace(msg, undefOrError(e))
      override def trace(msg: String): Unit = jsLogger.trace(msg)
      override def debug(msg: String, e: Exception): Unit = jsLogger.debug(msg, undefOrError(e))
      override def debug(msg: String): Unit = jsLogger.debug(msg)
      override def info(msg: String, e: Exception): Unit = jsLogger.info(msg, undefOrError(e))
      override def info(msg: String): Unit = jsLogger.info(msg)
      override def warn(msg: String, e: Exception): Unit = jsLogger.warn(msg, undefOrError(e))
      override def warn(msg: String): Unit = jsLogger.warn(msg)
      override def error(msg: String, e: Exception): Unit = jsLogger.error(msg, undefOrError(e))
      override def error(msg: String): Unit = jsLogger.error(msg)
      override def fatal(msg: String, e: Exception): Unit = jsLogger.fatal(msg, undefOrError(e))
      override def fatal(msg: String): Unit = jsLogger.fatal(msg)
    
      override def enableServerLogging(url: String): Unit = {
        if(ajaxAppender == null) {
          ajaxAppender = new AjaxAppender(url)
          ajaxAppender.addHeader("Content-Type", "application/json")
          ajaxAppender.setLayout(new JsonLayout)
          jsLogger.addAppender(ajaxAppender)
    
        }
      }
    
      override def disableServerLogging():Unit = {
        if(ajaxAppender != null) {
          jsLogger.removeAppender(ajaxAppender)
          ajaxAppender = null
        }
      }
    }
    

    当我尝试sbt运行时,我收到以下错误消息:

    > run
    [info] Running tutorial.webapp.TutorialApp
    Before getLogger...
    TypeError: undefined is not an object (evaluating '$g["log4javascript"]["getLogger"]')
    
      /tmp/phantomjs-launcher8416853343047081941.js:9 in onError
    
    
      /tmp/phantomjs-launcher8416853343047081941.js:11 in onError
      file:///home/jk/workspace/scalajs-tutorial-0.6.x/target/scala-2.11/scala-js-tutorial-fastopt.js:1085 (in function "getLogger__T__Ltutorial_logger_Logger")
    
      /tmp/phantomjs-launcher8416853343047081941.js:13
      file:///home/jk/workspace/scalajs-tutorial-0.6.x/target/scala-2.11/scala-js-tutorial-fastopt.js:1969 (in function "init___")
    
      /tmp/phantomjs-launcher8416853343047081941.js:13
      file:///home/jk/workspace/scalajs-tutorial-0.6.x/target/scala-2.11/scala-js-tutorial-fastopt.js:2005 (in function "$m_Ltutorial_webapp_TutorialApp$")
    
      /tmp/phantomjs-launcher8416853343047081941.js:13
      file:///tmp/phantomjs-launcher-webpage6476048362931659173.html:9541
    
      /tmp/phantomjs-launcher8416853343047081941.js:13
    org.scalajs.jsenv.ExternalJSEnv$NonZeroExitException: PhantomJS exited with code 2
        at org.scalajs.jsenv.ExternalJSEnv$AbstractExtRunner.waitForVM(ExternalJSEnv.scala:96)
        at org.scalajs.jsenv.ExternalJSEnv$ExtRunner.run(ExternalJSEnv.scala:143)
        at org.scalajs.sbtplugin.ScalaJSPluginInternal$.org$scalajs$sbtplugin$ScalaJSPluginInternal$$jsRun(ScalaJSPluginInternal.scala:479)
        at org.scalajs.sbtplugin.ScalaJSPluginInternal$$anonfun$45$$anonfun$apply$27$$anonfun$apply$28.apply(ScalaJSPluginInternal.scala:539)
        at org.scalajs.sbtplugin.ScalaJSPluginInternal$$anonfun$45$$anonfun$apply$27$$anonfun$apply$28.apply(ScalaJSPluginInternal.scala:533)
        at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
    [trace] Stack trace suppressed: run last compile:run for the full output.
    [error] (compile:run) org.scalajs.jsenv.ExternalJSEnv$NonZeroExitException: PhantomJS exited with code 2
    [error] Total time: 51 s, completed Jan 13, 2016 9:08:34 PM
    

    同样,sbt测试也将失败,并显示相同的错误消息:TypeError:undefined不是对象(计算'$g[“log4javascript”][“getLogger”]')

    如果我在build.sbt中注释以下行

    //scalaJSStage in Global := FastOptStage // If NOT commented out: Uses Phantom, if IS commented out: Uses Rhino
    

    然后我从sbt运行中得到以下结果:

    > run
    [info] Running tutorial.webapp.TutorialApp
    Before getLogger...
    After getLogger...
    [success] Total time: 4 s, completed Jan 13, 2016 9:16:39 PM
    

    当文件scalajs教程fastopt时,该脚本也适用于浏览器。html被加载,按钮出现,单击时控制台上会出现新的“You clicked the button”文本和“button clicked…”。同样,sbt测试成功。

    幻影版本是2.0.1开发版。

    如何使代码与Phantom.js一起工作?

    编辑: 我将Phantom降级到2.0.0版本,但错误消息保持不变。

    1 回复  |  直到 9 年前
        1
  •  2
  •   frankiesaurus    9 年前

    我也遇到了这个问题,遵循同样的教程。事实证明,问题的根本原因是在log4javascript代码中,有嵌入html的字符串(其中包含更多的javascript)。我不知道这背后的原因是什么…总之,PhantomJS测试运行程序的工作原理是将所有js代码和依赖项一起输出到一个html文件中(在脚本标记中);当javascript解释器看到未转义 </script> 标签,它出错了。(顺便说一下,我发现这一点的方法是在我的临时文件中找到phantomjs launcher-*.html页面,然后在Chrome中打开它)。

    无论如何,在查看了log4javascript的源代码并注意到它托管在sourceforge上,并且已经有9年没有更新了(源代码控制是CVS…)之后,我们决定使用不同的日志框架实现,因为能够在PhantomJS中运行测试对我们来说很重要。

    非常令人沮丧,因为您有点期待日志框架是“只起作用”的东西之一。