Ideas for measuring page load time

Question: how do you measure page load time, what technique do you use, what recommendations can you give, and what positive and negative experience do you have.

The problem is that even light pages in jsf can load up to 10 seconds. These pages do not require any ratings, rendering resources, etc. The obvious answer is that it is in the rendering queue ... OK, but maybe something else?

It seems like you need to measure the time starting from the request, including rendering, ratings, data transfer time, rendering time on the client side, etc.

Do you have good working methods, tool chains, tools for the full page life in JSF?

Used Glassfish-3, JSF-2. Machine with 64 processors.

+4
source share
2 answers

Here is a simple custom solution that I just made and tested. It provides some statistics on runtime and runtime. Of course, all information is available only from server processing.

Example result:

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 

The implementation is quite complicated. First you have fine-tuning PhaseListener inside faces-config.xml

 <lifecycle> <phase-listener>com.spectotechnologies.jsf.phaselisteners.PhaseProcessesAnalyserListener</phase-listener> </lifecycle> 

Here is a helper class that stores information about each processing:

 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; } } 

Here is a class with business logic , note that at the moment the statistics will only grow and will never be deleted, it can be a good update to save only the last hour, for example:

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

Here is the famous PhaseListener where information is tracked:

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

Finally, here is a simple page that I created to display statistics. A good improvement would be to add averages to process the pages, as well as the processing time at a time.

Bean code:

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

View code:

 <!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> 

UPDATE 1:

  • URL parameters added
  • 250 log limit added
+6
source

The following solution is based on, hosted by Alexandre Lavoie , but applicable to ADF individuals

PhaseProcessingTimeAnalyserListener

 import java.io.Serializable; import java.net.URLEncoder; import java.util.Enumeration; import java.util.Map; import javax.faces.context.FacesContext; import javax.servlet.http.HttpServletRequest; import oracle.adf.controller.v2.lifecycle.Lifecycle; import oracle.adf.controller.v2.lifecycle.PagePhaseEvent; import oracle.adf.controller.v2.lifecycle.PagePhaseListener; import oracle.adf.share.ADFContext; import com.mhis.posm.web.lifecycle.helper.PhaseProcessor; /** * Class PhaseProcessingTimeAnalyserListener calculates the execution time of each Phase of ADF * @author TapasB */ public class PhaseProcessingTimeAnalyserListener implements PagePhaseListener, Serializable { private static final long serialVersionUID = 6814928970314659328L; /**  * Method beforePhase notifies the listener before the execution of a specific phase of the ADF Page Lifecycle.  * @author TapasB  * @param phaseEvent  * @see oracle.adf.controller.v2.lifecycle.PagePhaseListener#beforePhase(oracle.adf.controller.v2.lifecycle.PagePhaseEvent)  */ @Override public void beforePhase(PagePhaseEvent phaseEvent) {  int phaseId = phaseEvent.getPhaseId();  String phaseName = Lifecycle.getPhaseName(phaseId);  PhaseProcessor.getInstance().process(getRequestId(), getURL(), phaseId, phaseName, PhaseProcessor.PhaseType.BEGIN); } /**  * Method afterPhase notifies the listener after the execution of a specific phase of the ADF Page Lifecycle.  * @author TapasB  * @param phaseEvent  * @see oracle.adf.controller.v2.lifecycle.PagePhaseListener#afterPhase(oracle.adf.controller.v2.lifecycle.PagePhaseEvent)  */ @Override public void afterPhase(PagePhaseEvent phaseEvent) {  int phaseId = phaseEvent.getPhaseId();  String phaseName = Lifecycle.getPhaseName(phaseId);  PhaseProcessor.getInstance().process(getRequestId(), getURL(), phaseId, phaseName, PhaseProcessor.PhaseType.END); } /**  * Method getRequestId generates and returns an unique ID value for each Request  * @author TapasB  * @return requestId  */ private Integer getRequestId() {  @SuppressWarnings("unchecked")  Map<String, Object> requestScope = ADFContext.getCurrent().getRequestScope();  Integer requestId = (Integer) requestScope.get("requestId");  if (requestId == null) {   requestId = PhaseProcessor.getInstance().getNextRequestId();   requestScope.put("requestId", requestId);  }  return requestId; } /**  * Method getURL returns the URL in which the application is requested  * @author TapasB  * @return a String URL  */ private String getURL() {  Enumeration<String> parameterNames = null;  String parameterName = null;  StringBuilder urlBuilder = new StringBuilder();  Object request = FacesContext.getCurrentInstance().getExternalContext().getRequest();  try {   if (request instanceof HttpServletRequest) {    HttpServletRequest servletRequest = (HttpServletRequest) request;    urlBuilder.append(servletRequest.getRequestURL().toString());    parameterNames = servletRequest.getParameterNames();    if (parameterNames.hasMoreElements()) {     if (!urlBuilder.toString().contains("?")) {      urlBuilder.append("?");     } else {      urlBuilder.append("&");     }    }    while (parameterNames.hasMoreElements()) {     parameterName = parameterNames.nextElement();     urlBuilder.append(parameterName);     urlBuilder.append("=");     urlBuilder.append(URLEncoder.encode(servletRequest.getParameter(parameterName), "UTF-8"));     if (parameterNames.hasMoreElements()) {      urlBuilder.append("&");     }    }   }  } catch (Exception ex) {  }  return urlBuilder.toString(); } } 

Phase processor

 import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import oracle.adf.share.ADFContext; import com.edifixio.osrd.generic.log.Log; /** * Class PhaseProcessor processes the Phase execution time * @author TapasB */ public class PhaseProcessor implements Serializable { private static final long serialVersionUID = 6658181867505616109L; private List<Phase> phases = new ArrayList<Phase>(); private AtomicInteger nextRequestId = new AtomicInteger(1); /**  * Constructor PhaseProcessor is private  * @author TapasB  */ private PhaseProcessor() { } /**  * Class PhaseType  * @author TapasB  */ public static enum PhaseType {  BEGIN,  END; } /**  * Method getInstance returns a Session Instance of this class  * @author TapasB  * @return an PhaseProcessor instance  */ public static PhaseProcessor getInstance() {  @SuppressWarnings("unchecked")  Map<String, Object> sessionScope = ADFContext.getCurrent().getSessionScope();  PhaseProcessor phaseProcessor = (PhaseProcessor) sessionScope.get("phases");  if (phaseProcessor == null) {   phaseProcessor = new PhaseProcessor();   sessionScope.put("phases", phaseProcessor);  }  return phaseProcessor; } /**  * Method process processes the {@link Phase}  * @author TapasB  * @param requestId - the unique ID for each request  * @param url - the url in which the application is requested  * @param phaseId - ADF Phase ID  * @param phaseName - ADF Phase Name  * @param phaseType - BEGIN or END  */ public void process(int requestId, String url, int phaseId, String phaseName, PhaseType phaseType) {  Phase phase;  switch (phaseType) {   case BEGIN:    phase = new Phase();    phase.setRequestId(requestId);    phase.setUrl(url);    phase.setPhaseId(phaseId);    phase.setPhaseName(phaseName);    phase.setStartTime(new Date());    phases.add(phase);    Log.info(this, "The Phase: " + phase.getPhaseName(phaseId) + " begins. Requested URL: " + phase.getUrl());    break;   case END:    ListIterator<Phase> phaseIterator = phases.listIterator(phases.size());    while (phaseIterator.hasPrevious()) {     phase = phaseIterator.previous();     if (phase.getRequestId() == requestId && phase.getPhaseId() == phaseId && phase.getPhaseName().equals(phaseName)) {      phase.setEndTime(new Date());      Log.info(this, "The Phase: " + phase.getPhaseName(phaseId) + " ends with execution time: '" + (phase.getEndTime().getTime() - phase.getStartTime().getTime()) + "' millisecondes. Requested URL: " + phase.getUrl());      phaseIterator.remove();      break;     }    }    break;  } } /**  * Method getNextRequestId returns and increment the unique ID  * @author TapasB  * @return the ID  */ public Integer getNextRequestId() {  return nextRequestId.getAndIncrement(); } } 

phase

 import java.io.Serializable; import java.util.Date; import oracle.adf.controller.faces.lifecycle.JSFLifecycle; /** * Class Phase represent a phase * @author TapasB */ public class Phase implements Serializable { private static final long serialVersionUID = -461595462579265128L; private int requestId; private String url; private Date startTime; private Date endTime; private int phaseId; private String phaseName; /**  * Constructor Phase is default  * @author TapasB  */ public Phase() { } /**  * Method getRequestId returns requestId  * @author TapasB  * @return requestId  */ public int getRequestId() {  return requestId; } /**  * Method setRequestId sets the requestId  * @author TapasB  * @param requestId the requestId to set  */ public void setRequestId(int requestId) {  this.requestId = requestId; } /**  * Method getUrl returns url  * @author TapasB  * @return url  */ public String getUrl() {  return url; } /**  * Method setUrl sets the url  * @author TapasB  * @param url the url to set  */ public void setUrl(String url) {  this.url = url; } /**  * Method getStartTime returns startTime  * @author TapasB  * @return startTime  */ public Date getStartTime() {  return startTime; } /**  * Method setStartTime sets the startTime  * @author TapasB  * @param startTime the startTime to set  */ public void setStartTime(Date startTime) {  this.startTime = startTime; } /**  * Method getEndTime returns endTime  * @author TapasB  * @return endTime  */ public Date getEndTime() {  return endTime; } /**  * Method setEndTime sets the endTime  * @author TapasB  * @param endTime the endTime to set  */ public void setEndTime(Date endTime) {  this.endTime = endTime; } /**  * Method getPhaseId returns phaseId  * @author TapasB  * @return phaseId  */ public int getPhaseId() {  return phaseId; } /**  * Method setPhaseId sets the phaseId  * @author TapasB  * @param phaseId the phaseId to set  */ public void setPhaseId(int phaseId) {  this.phaseId = phaseId; } /**  * Method getPhaseName returns phaseName  * @author TapasB  * @return phaseName  */ public String getPhaseName() {  return phaseName; } /**  * Method setPhaseName sets the phaseName  * @author TapasB  * @param phaseName the phaseName to set  */ public void setPhaseName(String phaseName) {  this.phaseName = phaseName; } /**  * Method getPhaseName returns the name of the Phase  * @author TapasB  * @param phaseId  * @return the phase name  */ public String getPhaseName(int phaseId) {  if (phaseId == JSFLifecycle.INIT_CONTEXT_ID) {   return "INIT_CONTEXT";  } else if (phaseId == JSFLifecycle.PREPARE_MODEL_ID) {   return "PREPARE_MODEL";  } else if (phaseId == JSFLifecycle.APPLY_INPUT_VALUES_ID) {   return "APPLY_INPUT_VALUES";  } else if (phaseId == JSFLifecycle.VALIDATE_INPUT_VALUES_ID) {   return "VALIDATE_INPUT_VALUES";  } else if (phaseId == JSFLifecycle.PROCESS_UPDATE_MODEL_ID) {   return "PROCESS_UPDATE_MODEL";  } else if (phaseId == JSFLifecycle.VALIDATE_MODEL_UPDATES_ID) {   return "VALIDATE_MODEL_UPDATES";  } else if (phaseId == JSFLifecycle.PROCESS_COMPONENT_EVENTS_ID) {   return "PROCESS_COMPONENT_EVENTS";  } else if (phaseId == JSFLifecycle.METADATA_COMMIT_ID) {   return "METADATA_COMMIT";  } else if (phaseId == JSFLifecycle.PREPARE_RENDER_ID) {   return "PREPARE_RENDER";  } else if (phaseId == JSFLifecycle.JSF_RESTORE_VIEW_ID) {   return "RESTORE_VIEW";  } else if (phaseId == JSFLifecycle.JSF_APPLY_REQUEST_VALUES_ID) {   return "JSF_APPLY_REQUEST_VALUES";  } else if (phaseId == JSFLifecycle.JSF_PROCESS_VALIDATIONS_ID) {   return "JSF_PROCESS_VALIDATIONS";  } else if (phaseId == JSFLifecycle.JSF_UPDATE_MODEL_VALUES_ID) {   return "JSF_UPDATE_MODEL_VALUES";  } else if (phaseId == JSFLifecycle.JSF_INVOKE_APPLICATION_ID) {   return "JSF_INVOKE_APPLICATION";  } else {   return "JSF_RENDER_RESPONSE";  } } } 

See here for more information.

+1
source

Source: https://habr.com/ru/post/1481272/


All Articles