序列化java.lang.Throwable以及堆栈跟踪,导致链和相关的堆栈跟踪

时间:2014-02-26 18:03:47

标签: java exception gwt serialization stack-trace

我正在编写 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 实例不那么受欢迎。实际上我找不到图书馆和博客,详细说明如何实现这一点。

有没有人坚持并解决过这样的问题?有没有针对这个问题的建议解决方案?

提前致谢!

3 个答案:

答案 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);
}