Take the community feedback survey now.

Anders Hattestad
Jun 5, 2012
  3863
(0 votes)

Log how long a page takes to generate

After a project goes live there is often some debate if the site is fast enough. With today’s heavy use of JavaScript a lot is done after the page is loaded so it’s difficult for a user to actually know if it’s the rendering/JavaScript or the page generating that takes time.

In a project of mine we had terrible lag, and load time experienced for the users was very high. I wrote a small report that saved hourly the count of load time grouped by 1,2,3,4,5+ seconds.

image

After I had let that code run for some hours I saw that the generating time from the server was not the problem. The actually problem was that the client didn’t cache the image resource files that were in use. I had overlooked the new (?) section in IIS7, so I changed my web.config to

    <caching>
      <profiles>
        <add extension=".gif" policy="CacheForTimePeriod" kernelCachePolicy="DontCache" duration="01:01:00" location="Any" />
        <add extension=".png" policy="CacheForTimePeriod" kernelCachePolicy="DontCache" duration="01:01:00" location="Any" />
        <add extension=".js" policy="CacheForTimePeriod" kernelCachePolicy="DontCache" duration="01:01:00" location="Any" />
        <add extension=".css" policy="CacheForTimePeriod" kernelCachePolicy="DontCache" duration="01:01:00" location="Any" />
        <add extension=".jpg" policy="CacheForTimePeriod" kernelCachePolicy="DontCache" duration="01:01:00" location="Any" />
        <add extension=".jpeg" policy="CacheForTimePeriod" kernelCachePolicy="DontCache" duration="01:01:00" location="Any" />
      </profiles>
    </caching>

And the clients got a huge boost in load time.

The little plugin works like this:

In my master page I added this code to Render

Code Snippet
  1. public partial class UDMaster : System.Web.UI.MasterPage,IHaveProperties
  2. {
  3.     DateTime start = DateTime.Now;
  4.  
  5.     protected override void Render(HtmlTextWriter writer)
  6.     {
  7.         if (PageTimesGrouped.SavePageTimesGrouped())
  8.         {
  9.             PageTimesGrouped.Add(((TimeSpan)(DateTime.Now - start)));
  10.         }
 
The Dynamic DataStore class looks like this. I save only one row pr hour so this code will not generate a lot of rows. It will only save down to the DDS base if it gets a request for a new hour. 
Code Snippet
  1. [EPiServerDataStore(AutomaticallyRemapStore = true, AutomaticallyCreateStore = true)]
  2. public class PageTimesGrouped : BaseData<PageTimesGrouped>
  3. {
  4.         [EPiServerDataIndex]
  5.     public DateTime Date { get; set; }
  6.  
  7.     public int Bellow1s { get; set; }
  8.     public int Count_1s_2s { get; set; }
  9.     public int Count_2s_3s { get; set; }
  10.     public int Count_3s_4s { get; set; }
  11.     public int Count_4s_5s { get; set; }
  12.     public int Above5s { get; set; }
  13.  
  14.        
  15.     static PageTimesGrouped _currentTime;
  16.     public static PageTimesGrouped CurrentTime()
  17.     {
  18.         var now = DateTime.Now;
  19.         if (_currentTime == null)
  20.         {
  21.             _currentTime = new PageTimesGrouped() { Date = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0), Bellow1s = 0, Count_1s_2s = 0, Count_2s_3s = 0, Count_3s_4s = 0, Count_4s_5s = 0, Above5s = 0 };
  22.         }
  23.         if (_currentTime.Date.Hour != now.Hour)
  24.         {
  25.             _currentTime.Save();
  26.             _currentTime = new PageTimesGrouped() { Date = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0), Bellow1s = 0, Count_1s_2s = 0, Count_2s_3s = 0, Count_3s_4s = 0, Count_4s_5s = 0, Above5s = 0 };
  27.         
  28.         }
  29.         return _currentTime;
  30.     }
  31.     public static void Add(TimeSpan span)
  32.     {
  33.         try
  34.         {
  35.             var a = CurrentTime();
  36.             if (span.TotalMilliseconds < 1000)
  37.                 a.Bellow1s++;
  38.             else if (span.TotalMilliseconds < 2000)
  39.                 a.Count_1s_2s++;
  40.             else if (span.TotalMilliseconds < 3000)
  41.                 a.Count_2s_3s++;
  42.             else if (span.TotalMilliseconds < 4000)
  43.                 a.Count_3s_4s++;
  44.             else if (span.TotalMilliseconds < 5000)
  45.                 a.Count_4s_5s++;
  46.             else
  47.                 a.Above5s++;
  48.         }
  49.         catch { }
  50.     }
  51.     public static bool SavePageTimesGrouped()
  52.     {
  53.         string tmp = System.Web.Configuration.WebConfigurationManager.AppSettings.Get("SavePageTimesGrouped");
  54.         if (!string.IsNullOrEmpty(tmp))
  55.             return true;
  56.         return false;
  57.     }
  58.     public decimal RequestPrMin
  59.     {
  60.         get
  61.         {
  62.             var count = Bellow1s + Count_1s_2s + Count_2s_3s + Count_3s_4s + Count_4s_5s + Above5s;
  63.             var now = DateTime.Now;
  64.             var naa = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0);
  65.             if (Date > now.AddHours(1))
  66.                 return count / (60);
  67.             var a = now.Minute - Date.Minute;
  68.             if (a > 0)
  69.                 return count / a;
  70.             return -1;
  71.         }
  72.     }
  73.  
  74. }
 The Report plugin looks like this

Code Snippet
  1. [GuiPlugIn(
  2.     Area = PlugInArea.ReportMenu,
  3.     DisplayName = "ShowPageInfo grouped",
  4.     Url = "~/Custom/AdminPages/ShowPageTimesV2.aspx")]
  5. public partial class ShowPageTimesV2 : System.Web.UI.Page
  6. {
  7.     protected void Page_Load(object sender, EventArgs e)
  8.     {
  9.         Bind();
  10.     }
  11.  
  12.     void Bind()
  13.     {
  14.         CurrentList.DataSource = new object[] { PageTimesGrouped.CurrentTime() };
  15.         CurrentList.DataBind();
  16.         List2.DataSource = (from item in PageTimesGrouped.Items orderby item.Date descending select item).Take(100);
  17.         List2.DataBind();
  18.     }
  19. }

with front code like this

Code Snippet
  1. <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ShowPageTimesV2.aspx.cs" Inherits="UDFase1.Custom.AdminPages.ShowPageTimesV2" %>
  2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  3. <html xmlns="http://www.w3.org/1999/xhtml">
  4. <body>
  5.     <form id="form1" runat="server">
  6.     <div>
  7.    SavePageTimesGrouped=<%=Itera.Data.PageTimesGrouped.SavePageTimesGrouped()%><br />
  8.    
  9.    <asp:Literal ID="DebugLit" runat="server" />
  10.  
  11.     <table border="1">
  12.                 <tr>
  13.                     <th>Dato</th>
  14.                     <th>Less 1s</th>
  15.                     <th>1s-2s</th>
  16.                     <th>2s-3s</th>
  17.                     <th>3s-4s</th>
  18.                     <th>4s-5s</th>
  19.                     <th>Abvoe 5s</th>
  20.                 </tr>
  21.  
  22.      <asp:Repeater ID="CurrentList" runat="server">
  23.  
  24.         <ItemTemplate>
  25.             <tr>
  26.                 <td><%#Eval("Date") %></td>
  27.                <td><%#Eval("Bellow1s")%></td>
  28.                 <td><%#Eval("Count_1s_2s")%></td>
  29.                 <td><%#Eval("Count_2s_3s")%></td>
  30.                 <td><%#Eval("Count_3s_4s")%></td>
  31.                 <td><%#Eval("Count_4s_5s")%></td>
  32.                 <td><%#Eval("Above5s")%></td>
  33.                 <td><%#Eval("RequestPrMin") %></td>
  34.  
  35.             </tr>
  36.         </ItemTemplate>
  37.      
  38.     </asp:Repeater>
  39.  
  40.  
  41.      <asp:Repeater ID="List2" runat="server">
  42.       
  43.         <ItemTemplate>
  44.             <tr>
  45.                 <td><%#Eval("Date") %></td>
  46.                <td><%#Eval("Bellow1s")%></td>
  47.                 <td><%#Eval("Count_1s_2s")%></td>
  48.                 <td><%#Eval("Count_2s_3s")%></td>
  49.                 <td><%#Eval("Count_3s_4s")%></td>
  50.                 <td><%#Eval("Count_4s_5s")%></td>
  51.                 <td><%#Eval("Above5s")%></td>
  52.                 <td><%#Eval("RequestPrMin") %></td>
  53.  
  54.             </tr>
  55.         </ItemTemplate>
  56.  
  57.     </asp:Repeater>
  58.      </table>
  59.     
  60.                           
  61.       
  62.     </div>
  63.     </form>
  64. </body>
  65. </html>

Jun 05, 2012

Comments

Tahir Naveed
Tahir Naveed Jun 6, 2012 03:52 PM

Hi,

Can you please provide BaseData class as well.

Thanks

Anders Hattestad
Anders Hattestad Jun 6, 2012 10:29 PM

Hi

Sorry for that. The base class is in this post
http://world.episerver.com/Blogs/Anders-Hattestad/Dates/2012/5/Cache-Dynamic-Data-Store-items/

Please login to comment.
Latest blogs
Content modeling for beginners

  Introduction Learning by Doing – Optimizely Build Series  is a YouTube series where I am building  a fictional  website called  TasteTrail , food...

Ratish | Sep 14, 2025 |

A day in the life of an Optimizely OMVP - Enhancing Search Relevance with Optimizely Graph: Synonyms and Pinned Results

When building search experiences for modern digital platforms, relevance is everything. Users expect search to understand their intent, even when...

Graham Carr | Sep 14, 2025

Optimizely CMS and HTML validation message: Trailing slash on void elements has no effect and interacts badly with unquoted attribute values.

When using the W3C Markup Validation Service, some annoying information messages pop up because Optimizely CMS adds the trailing slash to...

Tomas Hensrud Gulla | Sep 14, 2025 |

Turbocharge your strings - a case of display channels

When doing a routine performance test, during a CMS 12 upgrade, I was able to achieve 95% performance improvement. Let's look at SearchValues with ...

Stefan Holm Olsen | Sep 14, 2025 |