A critical vulnerability was discovered in React Server Components (Next.js). Our systems remain protected but we advise to update packages to newest version. Learn More

Anders Hattestad
Jun 5, 2012
  3940
(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
CMS Audiences - check all usage

Sometimes you want to check if an Audience from your CMS (former Visitor Group) has been used by which page(and which version of that page) Then yo...

Tuan Anh Hoang | Dec 12, 2025

Data Imports in Optimizely: Part 2 - Query data efficiently

One of the more time consuming parts of an import is looking up data to update. Naively, it is possible to use the PageCriteriaQueryService to quer...

Matt FitzGerald-Chamberlain | Dec 11, 2025 |

Beginner's Guide for Optimizely Backend Developers

Developing with Optimizely (formerly Episerver) requires more than just technical know‑how. It’s about respecting the editor’s perspective, ensurin...

MilosR | Dec 10, 2025

Optimizely PaaS Administrator Certification : Free for Everyone

Optimizely has recently launched a free PaaS Administrator Certification. https://academy.optimizely.com/student/activity/2958208-paas-cms-administ...

Madhu | Dec 9, 2025 |