Follow me

Tuesday, April 2, 2013

How to Calculate Percentiles Response Time From JMeter Aggregate Reports


In one of my project we used JMeter to test the performance of our web application.
As we know JMeter provide the percentile response time [default 90%] in the form of aggregate report, but our requirement was to generate the report having request and its given percentile response time [default 90%] in millisecond for current build and compare it with the benchmark report [STD response time set] and log the bug if percentile response time for particular request is greater than the expected.
So in this scenario we had needed to find out the way how JMeter calculate the response time for particular request.

Below are the steps and code I have written to achieve this.
·        When JMeter run the performance test it creates the resultAggregateReport_TestName.xml files in testResult folder
·        Below is the sample xml.


<?xml version="1.0" encoding="UTF-8"?>
<testResults version="1.2">
<httpSample t="2443" lt="2091" ts="1361266219161" s="false" lb="login initial" rc="200" rm="OK" tn="Login Logout Many Thread Group 1-1" dt="text" by="44691" ng="1" na="6">
  <assertionResult>
    <name>Response Assertion</name>
    <failure>false</failure>
    <error>false</error>
  </assertionResult>
  <assertionResult>
    <name>Duration Assertion</name>
    <failure>true</failure>
    <error>false</error>
    <failureMessage>The operation lasted too long: It took 2,443 milliseconds, but should not have lasted longer than 1,500 milliseconds.</failureMessage>
  </assertionResult>
</httpSample>
</testResults>


·        So for particular request JMeter capture the response time for lb from <httpSample> tags t attributes (where lb is label and t is Response/ Elapsed time (milliseconds)) and calculate the percentile response time which is by default 90 %
·        Below is the sample aggregate report showing % response time for particular lable/request







So we have the test result data available in the form of XML to calculate the percentile response time.
Below is the code to parse the aggregate report xml.
       /**
         * This method will parse the aggregate test result XML and return the
         * HASHMAP having request id/label as key and "," separated response time as
         * value
         *
         * @param path
         * @return
         */

        public HashMap<String, String> parseAggregateXML(File file,
                       HashMap<String, String> reportMap) {
               try {
                       tempReportMap = reportMap;
                       SAXParserFactory factory = SAXParserFactory.newInstance();
                       SAXParser saxParser = factory.newSAXParser();
                       DefaultHandler handler = new DefaultHandler() {
                              public void startElement(String uri, String localName,
                                             String qName, Attributes attributes)
                                             throws SAXException {

                                      if (qName.equals("httpSample")) {

                                             int length = attributes.getLength();
                                             // Each attribute
                                             for (int i = 0; i < length; i++) {

                                                     if (attributes.getQName(i).equals("lb")) {
                                                            key = attributes.getValue(i);
                                                     }

                                                     if (attributes.getQName(i).equals("t")) {
                                                            responseTime = attributes.getValue(i);

                                                     }
                                             }
                                             if (key != null && tempReportMap.containsKey(key)) {
                                                     String responseValues = tempReportMap.get(key);
                                                     responseValues = responseValues + ","
                                                                    + responseTime;
                                                     tempReportMap.put(key, responseValues);
                                                    
                                             } else if (key != null && responseTime != null) {
                                                     tempReportMap.put(key, responseTime);
                                                    
                                             }

                                      }

                              }

                              public void endElement(String uri, String localName,
                                             String qName) throws SAXException {

                                      if (qName.equals("httpSample")) {

                                                            key = null;
                                      }

                              }

                              public void characters(char ch[], int start, int length)
                                             throws SAXException {

                              }

                       };
                       InputStream in3 = new FileInputStream(file);
                       saxParser.parse(in3, handler);
               } catch (Exception e) {

               }
               return tempReportMap;

        }


As we have many xml as a part of aggregate test result we pass those xml to above parser which return the hash map as result having label/request url as key and “,” separated response time captured from all Xml for particular request.

Now we are ready with the data, next we will see how to calculate the percentile response time from this available data.

In below method we will pass the HashMap created in above method. Which convert the “,” separated value in double array and pass It to Percentile.evaluate(double [] array)  . The resultant double array map will hold the percentile response time for particular request.

I used org.apache.commons.math.stat.descriptive.rank.Percentile to calculate the percentile value.

With this custom method we can calculate up to 99.99 percentile response time for any request

        public HashMap<String, String> generatePercentileReportMap(HashMap<String, String> reportMap,
                       double percentage) {
               Iterator iter = reportMap.entrySet().iterator();
               Percentile responsePercentile = new Percentile(percentage);

               while (iter.hasNext()) {
                       Map.Entry pairs = (Map.Entry) iter.next();

                       String key = (String) pairs.getKey();
                       String values = (String) pairs.getValue();
                       double[] responseArray = ConversionUtil
                                      .convertStringArrayToDouble(values); //find it below
                       reportMap.put(key, Double.toString(ConversionUtil
                                      .roundTwoDecimals(responsePercentile
                                                     .evaluate(responseArray))));
               }
              
               return reportMap;
              

        }


      /**
        * To convert string array to double array
        * @param array
        * @return
        */
       public static double[] convertStringArrayToDouble(String array){
            
             String[] responseTimeArray = array.split(",");
             int length = responseTimeArray.length;
           final double[] responseArray = new double[length];
           for (int i=0; i < length; i++) {
              responseArray[i] = Double.parseDouble(responseTimeArray[i]);             
           }
           return responseArray;       
       }














Thursday, September 13, 2012

GRAILS DATABASE AND CONFIG PROPERTIES EXTERNALIZATION

In Grails' DataSource descriptor file located at grails-app/conf/DataSource.groovy.
This file contains the dataSource definition which includes the database settings like
driverClassName ,username ,password ,url  etc.....

Since Grails' DataSource definition is "environment aware", so you can do it like:

local {
                              dataSource {
                                             dbCreate = "update" // one of 'create', 'create-drop','update'
                                             url = "jdbc:mysql://localhost/dbname"                   
                                             username = "username"
                                             password = "password"
                              }
               }
production {
                              dataSource {
                                             dbCreate = "update" // one of 'create', 'create-drop','update'
                                             url = "jdbc:mysql://hostname/dbname"                  
                                             username = "username"
                                             password = "password"
                              }
               }

We can externalize these settings by creating the separate property file to set above parameter.
Below are the steps I follow to externalize the database parameter in grails application.

Step 1)
Set one System property which is path to your externalize property file or dir
                          JAVA_OPTS="$JAVA_OPTS -DextConfigDir=/path/to/ext/prop"
Step 2)
                       In Config.groovy add the below chunk of code. This will search for config files that get
                       merged into the main config.
                              def extConfig = System.properties.getProperty('extConfigDir')
                              grails.config.locations = ["file:${userHome}/.grails/${appName}-config.groovy"]
                              if (extConfig) {
                                             grails.config.locations << "file:${extConfig}/Config.properties"
                              }
                 
Step 3)
                    Creating the externalize property file.
                    Remove the datasource properties from DataSource.groovy and set this properties in
                    Config.properties mentioned below
                       Ex: - In DataSource.groovy
                              local {
                                             dataSource {
                                                            url = "jdbc:mysql://localhost/dbname"                   
                                                            username = "username"
                                                            password = "password"
                                             }
                              }
                              The Exteralize Config.properties file will be
                              dataSource.url=jdbc:mysql://localhost/dbname
                              dataSource.username=username
                              dataSource.password=password

 In this way we can externalize the data source as well as Config.groovy properties

Monday, September 10, 2012

Grails Log4j Externalization


Problem statement  

 As we know Grails application used its common configuration mechanism to
 provide the settings for log4j so for this we just need to add the log4j setting in
grails-pp/conf/Config.groovy but when this application goes live as a part of war we can’t set the logging level until we deploy the new war by making change is log4j settings.So externalization allows you to change the logs with just a server restart.

Below are the steps I perform to externalize the grails log4j settings.

1) Crete the log4j.xml by referring the log4j  block  in Config.groovy

   below is the xml representation of grails log4j block

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false" threshold="null">
    <appender name="stacktraceLog" class="org.apache.log4j.FileAppender">
        <param name="file" value="changeme/logs/stackTrace.log"/>
        <param name="name" value="stackTraceLog"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="conversionPattern" value="%d [%t] %-5p %c{2} %x - %m%n"/>
        </layout>
    </appender>
 <appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">
    <param name="File" value="changeme/logs/catalina.out" />
   <param name="Threshold" value="INFO"/>
    <param name="Append" value="true"/>
 <param name="DatePattern" value="'.'yyyy-MM-dd"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS Z} [Thread: %t  ] level=%-5p %m%n"/>
    </layout>
  </appender>
  <appender name="stdout" class="org.apache.log4j.ConsoleAppender">
        <param name="name" value="stdout"/>
 <param name="Target" value="System.out"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="conversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS Z} level=%-5p class=%c %X{uniqueId} %X{hostname} %X{requestURI} %X{clientIP} %X{userId} %X{realmId} %X{sessionId} %X{locale} %X{callingHost} %X{uniqueIdCallingHost} %X{asyncUserId} %X{isAsync} %X{taskId} %m%n"/>
        </layout>
    </appender>
     <!-- FOR STACKTRACE -->
    <logger name="StackTrace" additivity="false">
        <appender-ref ref="stacktraceLog"/>
    </logger>
    <!-- FROM GRAILS-->
    <logger name="org.codehaus.groovy.grails.web.servlet">
        <level value="ERROR"/>
    </logger>
    <logger name="org.codehaus.groovy.grails.web.pages">
        <level value="ERROR"/>
    </logger>
    <logger name="org.codehaus.groovy.grails.web.sitemesh">
        <level value="ERROR"/>
    </logger>
    <logger name="org.codehaus.groovy.grails.web.mapping.filter">
        <level value="ERROR"/>
    </logger>
    <logger name="org.codehaus.groovy.grails.web.mapping">
        <level value="ERROR"/>
    </logger>
    <logger name="org.codehaus.groovy.grails.commons">
        <level value="ERROR"/>
    </logger>
    <logger name="org.codehaus.groovy.grails.plugins">
        <level value="ERROR"/>
    </logger>
    <logger name="org.codehaus.groovy.grails.orm.hibernate">
        <level value="ERROR"/>
    </logger>
    <logger name="org.springframework">
        <level value="ERROR"/>
    </logger>
    <logger name="org.hibernate">
        <level value="INFO"/>
    </logger>
    <logger name="org">
        <level value="ERROR"/>
    </logger>
    <logger name="org.mortbay.log">
        <level value="WARN"/>
    </logger>
    <logger name="com.opensymphony">
        <level value="WARN"/>
    </logger>
   
    <!-- FROM GRAILS END -->
   <category additivity="true" name="net.sf">
      <priority value="INFO"/>
   </category>
    <category additivity="false" name="org.apache">
         <priority value="INFO"/>
 <appender-ref ref="FILE"/>
   </category>
    <root>
       <level value="DEBUG"/>
      <appender-ref ref="stdout"/>
       <appender-ref ref="APPLOG"/>
 <appender-ref ref="FILE"/>
    </root>
</log4j:configuration>


2) Remove the log4j block from the Config.groovy i.e.       log4j = {  }

3)  Add below log4jConfigurer bean in groovy class
     /grails-app/conf/spring/resources.groovy and set the path of       externalize log4j.xml

      log4jConfigurer(org.springframework.beans.factory.config.
       MethodInvokingFactoryBean){
        targetClass = "org.springframework.util.Log4jConfigurer"
        targetMethod = "initLogging"
        arguments = "path/to/external/log4j/log4j.xml"
      }


This will allow you to configure grails log externally !!!

How To Add Log4J For Logging In Tomcat [For grails application]

Purpose

The purpose of this document is to explain you the steps to enable the log4j logging in  apache  tomcat as you may know that tomcat used system logging API i.e. java.util.logging for its internal logging.

In one of my project I came across such scenario where I need to implement the log4j logger for tomcat logs as well as application [grails ] log.

So  below are the steps we perform to set up log4j logging on tomcat 7 for grails 1.3.7 application.

1) Move existing  tomcat conf/logging.properties to some other name

        mv logging.properties logging.properties-back

2) Download Tomcat juli adapters library to  tomcat/lib/

     wget http://mirror.sdunix.com/apache/tomcat/tomcat-7/v7.0.28/bin/extras/tomcat-juli-adapters.jar

3)  Download Tomcat juli library to tomcat/lib/

wget http://mirror.sdunix.com/apache/tomcat/tomcat-7/v7.0.28/bin/extras/tomcat-juli.jar

4)  To make log4j work with grails app using tomcat-juli.jar we need
org\apache\juli\logging\UserDataHelper.java  which is not available in extra/ tomcat-juli.jar [but available in default tomcat jar]  so we make a patch called tomcat-log4j-patch.jar. contain UserDataHelper.class [NOT UPLOADED] . Copy this jar in tomcat lib folder

5) Copy  log4j.jar in tomcat lib folder

6) Open the catalina.sh and find juli LogManager config section. In else part add the log4j.xml path

 #Set juli LogManager config file if it is present and an override has not been issued
 if [ -z "$LOGGING_CONFIG" ]; thenif [ -r "$CATALINA_BASE"/conf/logging.properties ]; then
 LOGGING_CONFIG="-Djava.util.logging.config.file=$CATALINA_BASE/conf/logging.properties"
 else
 LOGGING_CONFIG="-Dlog4j.configuration=file://$CATALINA_BASE/lib/log4j.xml" fi

7) Restart the server