这是我刚刚制作和测试的一个简单的自定义解决方案。它提供了一些关于执行和渲染时间的统计数据。当然,所有信息仅来自服务器端处理。
结果示例:
Date ID request URL Phase Execution time
2013-05-16 21:10:29.781 34 http://localhost:8080/web/page.jspx RESTORE_VIEW 1 15
2013-05-16 21:10:29.796 34 http://localhost:8080/web/page.jspx RENDER_RESPONSE 6 4438
2013-05-16 21:10:39.437 35 http://localhost:8080/web/page.jspx RESTORE_VIEW 1 16
2013-05-16 21:10:39.453 35 http://localhost:8080/web/page.jspx RENDER_RESPONSE 6 3937
实施非常简单。首先你必须在里面配置一个PhaseListenerfaces-config.xml
<lifecycle>
<phase-listener>com.spectotechnologies.jsf.phaselisteners.PhaseProcessesAnalyserListener</phase-listener>
</lifecycle>
这是保存每个处理信息的辅助类:
package com.spectotechnologies.website.common.helper;
import java.util.Date;
import javax.faces.event.PhaseId;
/**
*
* @author Alexandre Lavoie
*/
public class PhaseProcess
{
private int m_nIDRequest;
private String m_sURL;
private Date m_oStart;
private Date m_oEnd;
private PhaseId m_oPhase;
public void setIdRequest(int p_nIDRequest)
{
m_nIDRequest = p_nIDRequest;
}
public int getIdRequest()
{
return m_nIDRequest;
}
public void setUrl(String p_sURL)
{
m_sURL = p_sURL;
}
public String getUrl()
{
return m_sURL;
}
public void setStart(Date p_oStart)
{
m_oStart = p_oStart;
}
public Date getStart()
{
return m_oStart;
}
public void setEnd(Date p_oEnd)
{
m_oEnd = p_oEnd;
}
public Date getEnd()
{
return m_oEnd;
}
public void setPhase(PhaseId p_oPhase)
{
m_oPhase = p_oPhase;
}
public PhaseId getPhase()
{
return m_oPhase;
}
public long getExecutionTime()
{
long lExecutionTime = -1;
if(getEnd() != null)
{
lExecutionTime = getEnd().getTime() - getStart().getTime();
}
return lExecutionTime;
}
}
这是具有业务逻辑的类,请注意,目前统计信息只会增长并且永远不会被删除,这可能是一个很好的更新,只保留最后一个小时,例如:
package com.spectotechnologies.website.common.helper;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseId;
/**
*
* @author Alexandre Lavoie
*/
public class PhaseProcesses
{
private List<PhaseProcess> m_lItems = new ArrayList();
private int m_nNextIDRequest = 0;
public static PhaseProcesses getInstance()
{
FacesContext oFaces = FacesContext.getCurrentInstance();
PhaseProcesses oPhaseProcesses = (PhaseProcesses)oFaces.getExternalContext().getSessionMap().get("sessionPhaseProcesses");
if(oPhaseProcesses == null)
{
oPhaseProcesses = new PhaseProcesses();
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("sessionPhaseProcesses",oPhaseProcesses);
}
return oPhaseProcesses;
}
public void set(int p_nIDRequest, String p_sURL, PhaseId p_oPhase, int p_nType)
{
PhaseProcess oPhaseProcess;
// Phase start
switch(p_nType)
{
case 0:
// start
oPhaseProcess = new PhaseProcess();
oPhaseProcess.setIdRequest(p_nIDRequest);
oPhaseProcess.setUrl(p_sURL);
oPhaseProcess.setPhase(p_oPhase);
oPhaseProcess.setStart(new Date());
if(m_lItems.size() > 250)
{
m_lItems.remove(0);
}
m_lItems.add(oPhaseProcess);
break;
case 1:
// end
for(int nPhase = m_lItems.size() - 1;nPhase >= 0;nPhase--)
{
if(m_lItems.get(nPhase).getIdRequest() == p_nIDRequest && m_lItems.get(nPhase).getPhase() == p_oPhase)
{
m_lItems.get(nPhase).setEnd(new Date());
break;
}
}
break;
}
}
public List<PhaseProcess> getList()
{
return m_lItems;
}
public Integer getNextIDRequest()
{
return m_nNextIDRequest++;
}
}
这是著名的PhaseListener,其中跟踪信息:
package com.spectotechnologies.jsf.phaselisteners;
import com.spectotechnologies.website.common.helper.PhaseProcesses;
import java.net.URLEncoder;
import java.util.Enumeration;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.servlet.http.HttpServletRequest;
/**
*
* @author Alexandre Lavoie
*/
public class PhaseProcessesAnalyserListener implements PhaseListener
{
@Override
public PhaseId getPhaseId()
{
return PhaseId.ANY_PHASE;
}
@Override
public void beforePhase(PhaseEvent p_oEvent)
{
PhaseProcesses.getInstance().set(getIDRequest(),getURL(),p_oEvent.getPhaseId(),0);
}
@Override
public void afterPhase(PhaseEvent p_oEvent)
{
PhaseProcesses.getInstance().set(getIDRequest(),getURL(),p_oEvent.getPhaseId(),1);
}
private Integer getIDRequest()
{
Integer iIDRequest = (Integer)FacesContext.getCurrentInstance().getExternalContext().getRequestMap().get("idrequest");
if(iIDRequest == null)
{
iIDRequest = PhaseProcesses.getInstance().getNextIDRequest();
FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put("idrequest",iIDRequest);
}
return iIDRequest;
}
private String getURL()
{
Enumeration<String> lParameters;
String sParameter;
StringBuilder sbURL = new StringBuilder();
Object oRequest = FacesContext.getCurrentInstance().getExternalContext().getRequest();
try
{
if(oRequest instanceof HttpServletRequest)
{
sbURL.append(((HttpServletRequest)oRequest).getRequestURL().toString());
lParameters = ((HttpServletRequest)oRequest).getParameterNames();
if(lParameters.hasMoreElements())
{
if(!sbURL.toString().contains("?"))
{
sbURL.append("?");
}
else
{
sbURL.append("&");
}
}
while(lParameters.hasMoreElements())
{
sParameter = lParameters.nextElement();
sbURL.append(sParameter);
sbURL.append("=");
sbURL.append(URLEncoder.encode(((HttpServletRequest)oRequest).getParameter(sParameter),"UTF-8"));
if(lParameters.hasMoreElements())
{
sbURL.append("&");
}
}
}
}
catch(Exception e)
{
// Do nothing
}
return sbURL.toString();
}
}
最后,这是我创建的用于显示统计信息的简单页面。一个好的改进可能是增加页面处理的平均值,也增加了每个 idrequest 的处理时间。
豆代码:
package com.spectotechnologies.website.common.beans;
import com.spectotechnologies.website.common.helper.PhaseProcess;
import com.spectotechnologies.website.common.helper.PhaseProcesses;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
/**
*
* @author Alexandre Lavoie
*/
@ManagedBean
@RequestScoped
public class PagesStatisticsActions
{
public List<PhaseProcess> getList()
{
return PhaseProcesses.getInstance().getList();
}
}
查看代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view contentType="application/xhtml+xml">
<h:head>
<meta http-equiv="Content-Type" content="application/xhtml+xml;charset=UTF-8" />
</h:head>
<h:body>
<h:dataTable value="#{pagesStatisticsActions.list}" var="item">
<h:column>
<f:facet name="header">
Date
</f:facet>
<h:outputText value="#{item.start}">
<f:convertDateTime timeZone="America/Montreal" pattern="yyyy-MM-dd HH:mm:ss.SSS" />
</h:outputText>
</h:column>
<h:column>
<f:facet name="header">
ID request
</f:facet>
#{item.idRequest}
</h:column>
<h:column>
<f:facet name="header">
URL
</f:facet>
#{item.url}
</h:column>
<h:column>
<f:facet name="header">
Phase
</f:facet>
#{item.phase}
</h:column>
<h:column>
<f:facet name="header">
Execution time (ms)
</f:facet>
#{item.executionTime}
</h:column>
</h:dataTable>
</h:body>
</f:view>
</html>
更新 1:
- 在 URL 中添加参数
- 添加了 250 条日志的限制