4

使用GWT,我已将我的服务器部署到Tomcat中。这很好用,但是当GWT抛出异常时,弹出窗口会向客户端显示异常的堆栈跟踪。

在开发模式下,这工作正常。在 Tomcat 中,我得到以下堆栈跟踪。

为什么以及如何解决这个问题?

Unknown.Le(StackTraceCreator.java:168)
Unknown.Jd(StackTraceCreator.java:421)
Unknown.NT(Exception_FieldSerializer.java:16)
Unknown.g1(SerializerBase.java:55)
Unknown.b1(SerializerBase.java:112)
Unknown.D$(AbstractSerializationStreamReader.java:119)
Unknown.uAc(CustomException_FieldSerializer.java:39)
Unknown.uBc(ServerSideException_FieldSerializer.java:12)
Unknown.f1(SerializerBase.java:46)
Unknown._0(SerializerBase.java:92)
Unknown.D$(AbstractSerializationStreamReader.java:119)
Unknown.B_(RequestCallbackAdapter.java:216)
Unknown._o(Request.java:287)

使用@Christian Kuetbach 的答案后,我现在得到了:

Unknown.com_google_gwt_core_client_impl_StackTraceCreator $ CollectorEmulated_ $ fillInStackTrace__Lcom_google_gwt_core_client_impl_StackTraceCreator $ CollectorEmulated_2Ljava_lang_Throwable_2V(StackTraceCreator.java:168)Unknown.java_lang_Throwable_Throwable__Ljava_lang_String_2Ljava_lang_Throwable_2V(StackTraceCreator.java:421)Unknown.com_google_gwt_user_client_rpc_StatusCodeException_StatusCodeException__ILjava_lang_String_2V(StatusCodeException.java:35)Unknown.com_google_gwt_user_client_rpc_impl_RequestCallbackAdapter_ $ onResponseReceived__Lcom_google_gwt_user_client_rpc_impl_RequestCallbackAdapter_2Lcom_google_gwt_http_client_Request_2Lcom_google_gwt_http_client_Response_2V(RequestCallbackAdapter.java:209)未知。com_google_gwt_http_client_Request_$fireOnResponseReceived__Lcom_google_gwt_http_client_Request_2Lcom_google_gwt_http_client_RequestCallback_2V(Request.java:287) Unknown.com_google_gwt_http_client_RequestBuilder$1_onReadyStateChange__Lcom_google_java_gwt_xhr_client_XMLHttpRequest_2V(RequestBuilder.java:395) Unknown.anonymous(HttpRequest:287)

请帮忙!

4

3 回答 3

5

为什么?

正如 Christian Kuetbach 所说,这是在 DevMode(您的代码在 Java 中执行)和 prod 模式(您的代码已编译为 JavaScript 并优化,包括重命名类和方法)之间的区别。

你如何解决这个问题?

你没有。一般来说,向用户显示堆栈跟踪不是一个好主意。更好的是通过将异常发送到服务器来记录异常(例如,使用java.util.logging来记录,以及SimpleRemoteLogHandler将日志发送到服务器,在服务器上使用 来记录java.util.logging)。

不过,有一些方法可以对堆栈跟踪进行反混淆,并且RemoteLoggingServiceImpl可以将 servlet 配置为自动执行此操作。
有关血腥细节,请参阅http://code.google.com/p/google-web-toolkit/wiki/WebModeExceptions

如果您不能或不想使用远程日志记录,那么您可以“手动”对堆栈跟踪进行反混淆:查看与排列对应的WEB-INF/deploy(默认位置,可以通过传递-deploy给 GWT 编译器来更改)中的文件(与*.cache.*浏览器加载的文件同名),它会告诉您该Le方法源自哪个 Java 方法。
但是你已经有了源文件名和行号,所以你真的不需要它,对吧?

于 2012-07-27T15:02:50.363 回答
5

在 GWT 文档中找到所有信息有点困难,您需要设置去混淆日志记录,所以这里是简短版本:

在您的模块文件 (.gwt.xml) 中,添加:

<inherits name="com.google.gwt.logging.Logging"/>
<set-property name="gwt.logging.simpleRemoteHandler" value="ENABLED" />
<set-property name="compiler.stackMode" value="emulated" />
<set-configuration-property name="compiler.emulatedStack.recordLineNumbers" 
     value="true" />

在客户端,使用类似的东西

import java.util.logging.Logger;

private static Logger rootLogger = Logger.getLogger("");
...
rootLogger.log(Level.SEVERE, "My message", e);

您不必RemoteLoggingServiceAsync在客户端创建实例 - 它由记录器自动使用,因为我们指定了<set-property name="gwt.logging.simpleRemoteHandler" value="ENABLED" />.

在服务器端,配置 RemoteLoggingServiceImpl。您必须告诉它,它在哪里可以找到符号映射,这将在使用 GWT 编译器参数进行编译时生成-extra /path/to/myExtraDir。我个人使用重写 RemoteLoggingServiceImpl 的方法,以允许从 web.xml 的<init-param>[ *]中指定目录

package mypackage.server;

public class ConfigurableRemoteLoggingServiceImpl extends RemoteLoggingServiceImpl {

  @Override
  public void init(final ServletConfig config) throws ServletException {
    super.init(config);

    final String symbolMapsDirectory = 
        config.getInitParameter("symbolMapsDirectory");
    setSymbolMapsDirectory(symbolMapsDirectory);
  }
}

web.xml中,像这样注册

<servlet>
    <servlet-name>remoteLogging</servlet-name>
    <servlet-class>mypackage.server.ConfigurableRemoteLoggingServiceImpl</servlet-class>

  <init-param>
    <param-name>symbolMapsDirectory</param-name>
    <param-value>/path/to/myExtraDir/mymodulename/symbolMaps</param-value>
  </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>remoteLogging</servlet-name>
    <url-pattern>/mymodulename/remote_logging</url-pattern>
</servlet-mapping>

用您自己的值替换/path/to/myExtraDir,mymodulenamemypackage,并且不要忘记使用-extra参数调用 GWT 编译器(请注意,您不必使用-style PRETTY或详细说明,它也适用于 OBF)。保留所有生成的符号映射:没有它们,反混淆将不起作用。由于每个新版本都会自动获得一个唯一的名称,因此您可以在构建时将它们全部收集在一个安全的中心位置。

[*] 我真的,真的很想知道,为什么 RemoteLoggingServiceImpl 自己不实现它!

于 2012-07-29T10:46:49.153 回答
1

这按预期工作,因为优化将删除方法和类名。

您可以编译为 PRETTY 或 DETAILED,以获得更好的可读性 Stacktraces。

还有模拟 Stacktraces 的可能性。

<set-property name="compiler.emulatedStack" value="true"/> 
<set-configuration-property name="compiler.emulatedStack.recordLineNumbers" value="true"/>
<set-configuration-property name="compiler.emulatedStack.recordFileNames" value="true"/> 

这对于生产用途来说是个坏主意,因为它会增加您的 javascript 的大小。

更新: 我看到一个 Exception_FieldSerializer 异常。

您是否尝试序列化某些不可序列化的东西?

没有默认构造函数的类是不可序列化的,不在您的客户端或共享包中的类。如果您尝试序列化异常,这可能是问题所在。

于 2012-07-27T13:28:37.737 回答