2014-02-26 33 views
1

我寫GWT應用程序,我需要發送java.lang.Throwable的的實例(含鏈的這原因和所有堆棧跟蹤分別)使用GWT RPC它使用標準的Java序列化機制(就我而言)。序列化的java.lang.Throwable帶有堆棧跟蹤一起,導致鏈相關的堆棧跟蹤

的問題是,當我通過從客戶下面的示例例外

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.所以stacktr王牌信息丟失連同原因鏈。發現

閱讀精湛的文章7 Tips for Exception Handling in GWT後指出,

異常中的堆棧跟蹤是短暫的,所以從客戶端到服務器(丟失,所以如果你需要在服務器端,發送作爲一個單獨的參數)

有點谷歌搜索後,我來到結論這一主題充分串行/使用標準Java序列化反序列化的實例java.lang.Throwable中技術是不太受歡迎。實際上我找不到有關如何實現這一目標的圖書館和博客。

有沒有人在之前解決過這個問題?有沒有建議解決這個問題的方法?

在此先感謝!

+0

GWT編譯爲JavaScript。一旦部署了應用程序,就不會得到與您期望的Java相同的異常。我錯過了什麼嗎? –

回答

2

好了,找到一個優雅和簡單的解決方案對我的問題:GWT 2.5。1有專門針對這些需求稱爲com.google.gwt.core.client.impl.SerializableThrowable與以下的JavaDoc設計的類:

的仿真的Throwable類不遞歸序列將Throwable和不序列堆棧跟蹤。這個類是一個替代方案,可以通過爲包含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.0com.google.gwt.core.client.impl.SerializableThrowable贊成com.google.gwt.core.shared.SerializableThrowable從第一個不同之處僅輕微,具有類似的工作已被棄用。

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(); 
    } 
} 
+0

謝謝你的回答!我想打印堆棧跟蹤字符串和發送字符串,但在** GWT **它提出了2個問題:1. GWT客戶端代碼不能使用** PrintWriter,StringWriter ** 2.堆棧跟蹤代碼**在客戶端模糊處理**並且需要在服務器端**進行**反混淆處理,因此在序列化期間保留標準的** StackTraceElement **類更受歡迎。 關於過時的堆棧跟蹤:** ** GWT **中的服務器和客戶**代碼應該**同時改變**,所以不應該是問題。 – Yura

+0

看到上面的修改解決方案! –

+0

這太棒了 - 非常感謝提示!仍然存在將此字符串反序列化回** StackTraceElement **實例的問題持續存在...... – Yura

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); 
} 
+0

謝謝你的回答!我考慮使用** GWT的**內置日誌機制,但我需要更多的靈活性,所以我決定使用我自己的服務。 – Yura