我正在编写 GWT 应用程序,我需要发送 java.lang.Throwable 的实例(其中包含原因的所有链接分别使用 GWT RPC 堆栈跟踪,它使用标准的Java序列化机制(据我所知)。
问题在于,当我从客户端传递以下示例异常时:
java.lang.RuntimeException (message=null, stacktrace A) caused by
java.io.IOException (message="Problems with io", stacktrace B) caused by
java.lang.IllegalStateException (message="Some error text", stacktrace C), cause=null
服务器上的我收到以下信息:
java.lang.RuntimeException (message="java.io.IOException: Problems with io", stacktrace X) cause=this
其中stacktrace X
只是堆栈跟踪,导致在服务器上反序列化此异常的位置,即不考虑原始堆栈跟踪A,B或C.因此堆栈跟踪信息丢失以及原因链。
阅读精湛的文章7 Tips for Exception Handling in GWT后,发现了
异常中的堆栈跟踪是暂时的,因此从客户端丢失到服务器(因此如果您在服务器端需要它,请将其作为单独的参数发送)
经过一些谷歌搜索后,我得出结论完全序列化/反序列化使用标准Java序列化技术的 java.lang.Throwable 实例不那么受欢迎。实际上我找不到图书馆和博客,详细说明如何实现这一点。
有没有人坚持并解决过这样的问题?有没有针对这个问题的建议解决方案?
提前致谢!
答案 0 :(得分:2)
好的,为我的问题找到了一个优雅而简单的解决方案:在 GWT 2.5.1 中,有一个专门为那些名为com.google.gwt.core.client.impl.SerializableThrowable
的需求设计的类,其中包含以下JavaDoc:
模拟的Throwable类不会递归地序列化Throwables,也不会序列化堆栈跟踪。此类是一种替代方法,可以通过为包含Throwable的类编写自定义序列化程序来使用。请参阅LogRecord_CustomFieldSerializer作为示例。
因此,代码片段解决了我的问题如下:
// client-side
LogServiceAsync logService = GWT.create(LogService.class);
GWT.setUncaughtExceptionHandler(new GWT.UncaughtExceptionHandler() {
@Override
public void onUncaughtException(final Throwable ex) {
// wrapping throwable in SerializableThrowable to preserve
// causes and stack traces upon serialization
SerializableThrowable serializableEx = new SerializableThrowable(ex);
// sending instance of SerializableThrowable to server
logService.log(serializableEx, callbackCodeDoesntMatter);
}
}
// server-side
public class LogServiceServlet extends RemoteServiceServlet implements LogService {
@Override
public void log(final SerializableThrowable ex) {
// getting original instance Throwable with preserved
// causes and stack traces
Throwable originalThrowable = ex.getThrowable();
originalThrowable.printStackTrace();
}
}
如果以这种方式实现,它会打印正确的堆栈跟踪信息以及正确的原因。
注意在 GWT 2.6.0 类com.google.gwt.core.client.impl.SerializableThrowable
中,不赞成使用com.google.gwt.core.shared.SerializableThrowable,这与第一个不同,只是略有不同与此类似。
答案 1 :(得分:1)
即使工作,我也不认为这样做是明智的。在序列化中,我们需要继续点击附加到对象的内容,并确保在反序列化时获得所需的所有类的正确版本,否则将失败。因此,依赖于运行环境,异常堆栈跟踪是不同的,平台,jvm版本,其他库不同....所以,将堆栈跟踪视为该环境的及时快照,除非在以后恢复它,否则无法重新引入到同一环境的时间。但是在您的要求中,它意图从客户端发送到服务器,因此,这将永远不会工作!最好的办法就是将其作为字符串捕获并保存为:
public static String getStackTrace(Throwable t) {
if (t == null) {
return "Exception not available.";
} else {
StringWriter stackTraceHolder = new StringWriter();
t.printStackTrace(new PrintWriter(stackTraceHolder));
return stackTraceHolder.toString();
}
}
如果你不能使用StringWriter,那么试试这个:
public static String getStackTrace(Throwable t) {
if (t == null) {
return "Exception not available.";
} else {
StringBuilder sb = new StringBuilder();
for (StackTraceElement element : t.getStackTrace()) {
sb.append(element.toString());
sb.append("\n");
}
return sb.toString();
}
}
答案 2 :(得分:1)
尝试 RemoteLoggingService 将日志从客户端发送到服务器端。以下是示例代码:
<强>的web.xml:强>
<servlet>
<servlet-name>remoteLogServlet</servlet-name>
<servlet-class>com.x.y.z.server.servlet.GwtRemoteLogging</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>remoteLogServlet</servlet-name>
<url-pattern>/context_path/remote_logging</url-pattern>
</servlet-mapping>
<强> GwtRemoteLogging.java:强>
import java.util.logging.Level;
import java.util.logging.LogRecord;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import com.google.gwt.logging.server.StackTraceDeobfuscator;
import com.google.gwt.logging.shared.RemoteLoggingService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import org.apache.log4j.Logger;
/**
* The Class GwtRemoteLogging.
*/
@SuppressWarnings("serial")
public class GwtRemoteLogging extends RemoteServiceServlet implements RemoteLoggingService {
/** The Constant logger. */
private StackTraceDeobfuscator deobfuscator = null;
private final static Logger logger = Logger.getLogger("logger_name");
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
/**
* Logs a Log Record which has been serialized using GWT RPC on the server.
*
* @return either an error message, or null if logging is successful.
*/
public final String logOnServer(LogRecord lr) {
try {
if (lr.getLevel().equals(Level.SEVERE)) {
logger.error(lr.getMessage(),lr.getThrown());
} else if (lr.getLevel().equals(Level.INFO)) {
logger.info(lr.getMessage(),lr.getThrown());
} else if (lr.getLevel().equals(Level.WARNING)) {
logger.warn(lr.getMessage(),lr.getThrown());
} else if (lr.getLevel().equals(Level.FINE)) {
logger.debug(lr.getMessage(),lr.getThrown());
} else {
logger.trace(lr.getMessage(),lr.getThrown());
}
} catch (Exception e) {
logger.error("Remote logging failed", e);
return "Remote logging failed, check stack trace for details.";
}
return null;
}
/**
* By default, this service does not do any deobfuscation. In order to do server side
* deobfuscation, you must copy the symbolMaps files to a directory visible to the server and
* set the directory using this method.
*
* @param symbolMapsDir
*/
public void setSymbolMapsDirectory(String symbolMapsDir) {
if (deobfuscator == null) {
deobfuscator = new StackTraceDeobfuscator(symbolMapsDir);
} else {
deobfuscator.setSymbolMapsDirectory(symbolMapsDir);
}
}
}
gwt.xml(继承日志记录和设置属性):
<inherits name="com.google.gwt.logging.Logging"/>
<set-property name="gwt.logging.logLevel" value="FINE"/>
<set-property name="gwt.logging.enabled" value="TRUE"/>
<!-- This handler sends log messages to the server, where they will be logged using the server side logging mechanism. -->
<set-property name="gwt.logging.simpleRemoteHandler" value="ENABLED" />
<!-- Logs by calling method GWT.log. These messages can only be seen in Development Mode in the DevMode window. -->
<set-property name="gwt.logging.developmentModeHandler" value="ENABLED" />
<!-- These messages can only be seen in Development Mode in the DevMode window. -->
<set-property name="gwt.logging.systemHandler" value="ENABLED" />
<!-- Logs to the popup which resides in the upper left hand corner of application when this handler is enabled. -->
<set-property name="gwt.logging.popupHandler" value="DISABLED" />
<!-- Logs to the javascript console, which is used by Firebug Lite (for IE), Safari and Chrome. -->
<set-property name="gwt.logging.consoleHandler" value="DISABLED"/>
<!-- Logs to the firebug console. -->
<set-property name="gwt.logging.firebugHandler" value="DISABLED" />
客户端代码:
public void log(String message,Throwable e) {
Logger logger = java.util.logging.Logger.getLogger("class_name");
logger.fine(name + ":" + message);
logger.info(name + ":" + message);
logger.log(Level.SEVERE, message, e);
logger.warning(message);
}