2016-01-13 69 views
0

我尝试使用ScalaJS和PhantomJS运行log4javascript,并在Rhino上运行时出现错误。如何在ScalaJS/PhantomJS上使用log4javascript

我把下面的ScalaJS例如:https://github.com/scala-js/scalajs-tutorial

和log4javascript我从另一个ScalaJS例子了: 更specificly这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" 

记录器文件位于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被加载时,按钮出现,当有点击是新的“您点击了按钮”文本和“点击了按钮...”o控制台。另外sbt测试成功。

幻影版本是2.0.1-开发。

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

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

回答

2

我也遇到了这个问题,遵循相同的教程。事实证明,问题的根源在于,在log4javascript代码中,有嵌入式html的字符串(其中包含更多的JavaScript)。我不知道这背后的推理可能是什么......无论如何,PhantomJS测试运行器只需将所有js代码和依赖关系一起放入html文件(在脚本标签内)即可工作;当javascript解释器看到一个未转义的标签时,它会出错。 (顺便说一下,我发现这一点,是通过在我的临时文件中找到phantomjs-launcher - *。html页面并在Chrome中打开它)。

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

非常令人沮丧,因为你期望日志框架是“正常工作”的那些东西之一。

+0

这很好听,关于PhantomJS。您选择了哪种日志框架,并且您是否设法使用PhantomJS? – user4955663

+0

最后,我和[Log4js](https://github.com/stritti/log4js)一起去了,因为它似乎与log4javascript非常相似。有一些工作涉及到将它打包成一个webjar(你可能不感兴趣),并为它编写一个ScalaJS外观,后来证明这是相对直接的,但我最终得到了它。为了快速解决问题,如果你现在不太喜欢登录,可以从项目中删除log4javascript。 – frankiesaurus

+0

其实我想知道你需要做什么才能让它与Phantom一起工作。你是如何打包的? 我也简单地查看了stritti的log4js,但注意到网站文档和实际的代码匹配很差,并且zip中没有“打包的”js文件。我还为节点找到了另一个log4js:https://github.com/nomiddlename/log4js-node。它也是基于stritti的工作,但现在分开了。 – user4955663