8

我想測量JSF應用程序的呈現時間。由於出於我的權力原因,應用程序不能使用日誌填充。在服務器請求後測量JSF視圖的呈現時間

因此,我的問題是,在使用任何瀏覽器執行包括後端(服務器)調用的特定操作之後,是否有任何方法可以測量應用程序的渲染時間?

到目前爲止,使用Chrome開發者工具之後,我發現了以下內容。在網絡選項卡上,每個請求都顯示「時間」。另外,在選擇某個條目之後,在「定時」選項卡上顯示更詳細的可視化。 現在,我可以從中看出,在這裏捕獲服務器的往返「等待」,但實際渲染時間又如何?

假設整個請求花了1秒,等待部分是500毫秒,我可以推斷它的渲染是1秒-500毫秒嗎?我不假定,那就是爲什麼我問這個問題。

長話短說,我需要從瀏覽器瞭解某個請求,服務器處理的時間以及實際UI渲染的時間。

任何提示將不勝感激。謝謝。

回答

10

你可以做到這一點與自定義ViewDeclarationLanguage,讓你測量createView()buildView()renderView()並在必要時restoreView()方法。

這裏有一個開球例如:

public class VdlLogger extends ViewDeclarationLanguageWrapper { 

    private static final Logger logger = Logger.getLogger(VdlLoggerFactory.class.getName()); 

    private ViewDeclarationLanguage wrapped; 

    public VdlLogger(ViewDeclarationLanguage wrapped) { 
     this.wrapped = wrapped; 
    } 

    @Override 
    public UIViewRoot createView(FacesContext context, String viewId) { 
     long start = System.nanoTime(); 
     UIViewRoot view = super.createView(context, viewId); 
     long end = System.nanoTime(); 
     logger.info(String.format("create %s: %.6fms", viewId, (end - start)/1e6)); 
     return view; 
    } 

    @Override 
    public void buildView(FacesContext context, UIViewRoot view) throws IOException { 
     long start = System.nanoTime(); 
     super.buildView(context, view); 
     long end = System.nanoTime(); 
     logger.info(String.format("build %s: %.6fms", view.getViewId(), (end - start)/1e6)); 
    } 

    @Override 
    public void renderView(FacesContext context, UIViewRoot view) throws IOException { 
     long start = System.nanoTime(); 
     super.renderView(context, view); 
     long end = System.nanoTime(); 
     logger.info(String.format("render %s: %.6fms", view.getViewId(), (end - start)/1e6)); 
    } 

    @Override 
    public ViewDeclarationLanguage getWrapped() { 
     return wrapped; 
    } 

} 

讓它運行,創建下面的工廠:

public class VdlLoggerFactory extends ViewDeclarationLanguageFactory { 

    private ViewDeclarationLanguageFactory wrapped; 

    public VdlLoggerFactory(ViewDeclarationLanguageFactory wrapped) { 
     this.wrapped = wrapped; 
    } 

    @Override 
    public ViewDeclarationLanguage getViewDeclarationLanguage(String viewId) { 
     return new VdlLogger(wrapped.getViewDeclarationLanguage(viewId)); 
    } 

    @Override 
    public ViewDeclarationLanguageFactory getWrapped() { 
     return wrapped; 
    } 

} 

而且在faces-config.xml如下注冊它:

<factory> 
    <view-declaration-language-factory>com.example.VdlLoggerFactory</view-declaration-language-factory> 
</factory> 

createView()是創建具體UIViewRoot實例ba的步驟sed <f:view><f:metadata>存在於視圖文件中。當使用Facelets(XHTML)作爲視圖時,在此步驟中,所有關聯的XHTML文件將由SAX解析器解析並緩存一段時間,如javax.faces.FACELETS_REFRESH_PERIOD中所定義。所以可能會發生這樣的情況,一段時間相對較慢,另一段時間很快。

buildView()是基於視圖(XHTML)組合填充JSF組件樹(getChildren()的)的步驟。在此步驟中,將執行所有標記處理程序(JSTL和朋友),並評估這些標記處理程序和組件的idbinding屬性中的所有EL表達式(有關詳細信息,另請參閱JSTL in JSF2 Facelets... makes sense?)。因此,如果在查看構建時間期間第一次構建支持bean並在@PostConstruct期間調用業務邏輯時,可能會發生這種情況,這很耗時。

renderView()是基於JSF組件樹和模型生成HTML輸出的步驟,從UIViewRoot#encodeAll()開始。因此,如果在查看渲染時間期間第一次構建支持bean並在@PostConstruct期間調用業務邏輯時,可能會發生這種情況,這很耗時。

如果支持bean在getter方法中而不是在@PostConstruct或任何其他一次性生命週期事件偵聽器中錯誤地執行業務邏輯,則可能會發生這種情況,這會消耗更多時間。另見Why JSF calls getters multiple times

+0

這只是...真棒。謝謝! – user2271933

+0

不客氣。在2.2中添加了 – BalusC

+0

[ViewDeclarationLanguageWrapper](http://docs.oracle.com/javaee/7/api/javax/faces/view/ViewDeclarationLanguageWrapper.html)。你認爲修改這個版本可以很容易地與2.1一起工作,還是我會更好地尋找另一種實現它的方式? –