User Activity Tracking in Power Pages using Azure Application Insights

πŸš€ User Activity Tracking in Power Pages using Azure Application Insights

πŸ“Œ Introduction

In today's enterprise applications, understanding how users interact with your system is extremely important. It helps organizations improve user experience, monitor engagement, and make better business decisions.

However, Power Pages does not provide detailed user tracking capabilities out of the box. We cannot directly know:

To solve this problem, we can integrate Azure Application Insights, which allows us to capture and analyze user behavior in real time.

In this blog, we will build a complete user activity tracking system from scratch, which captures every important user interaction.


🎯 What We Will Achieve


🧰 Prerequisites

Before starting, ensure you have:


βš™οΈ Step 1: Create Application Insights

Azure Application Insights is used to collect and store telemetry data from your application.

Steps:

  1. 1 Go to Azure Portal
  2. 2 Search for Application Insights
  3. 3 Click Create β€” fill in: Name, Resource Group, Region
  4. 4 Click Review + Create
πŸ”‘

Important

After creation, go to Overview and copy the Connection String. This is used to connect your portal to Azure.


🌐 Step 2: Add Script to Power Pages

Navigation:

Power Pages β€Ί Edit Portal β€Ί Web Templates β€Ί Header

Why Header?

Because it ensures tracking works on every page automatically.

JavaScript Β· Header Web Template
/* ── Azure Application Insights SDK ── */
<script src="https://js.monitor.azure.com/scripts/b/ai.2.min.js"></script>

<script>
  /* ── INITIALIZE ──────────────────────── */
  var appInsights = new Microsoft.ApplicationInsights.ApplicationInsights({
    config: {
      connectionString: "YOUR_CONNECTION_STRING",
      enableAutoRouteTracking: true
    }
  });
  appInsights.loadAppInsights();

  /* ── USER DETAILS ────────────────────── */
  var userId    = "{{ user.id }}";
  var userName  = "{{ user.fullname }}";
  var userEmail = "{{ user.emailaddress1 }}";

  if (userId) {
    appInsights.setAuthenticatedUserContext(userId);
  }

  /* ── PAGE VIEW ───────────────────────── */
  appInsights.trackPageView({
    name: document.title,
    uri: window.location.href
  });

  /* ── LOGIN TRACKING ──────────────────── */
  if (userId && !sessionStorage.getItem("loginTracked")) {
    appInsights.trackEvent({
      name: "User_SignIn",
      properties: {
        userId: userId, userName: userName, userEmail: userEmail,
        page: window.location.pathname, time: new Date().toISOString()
      }
    });
    sessionStorage.setItem("loginTracked", "true");
  }

  /* ── LOGOUT TRACKING ─────────────────── */
  window.addEventListener("beforeunload", function () {
    if (userId && sessionStorage.getItem("loginTracked")) {
      appInsights.trackEvent({
        name: "User_SignOut",
        properties: {
          userId: userId, userName: userName, userEmail: userEmail,
          page: window.location.pathname, time: new Date().toISOString()
        }
      });
      sessionStorage.removeItem("loginTracked");
    }
  });

  /* ── CLICK TRACKING ──────────────────── */
  document.addEventListener("click", function (e) {
    var el = e.target;
    while (el && el.tagName !== "A" && el.tagName !== "BUTTON") {
      el = el.parentElement;
    }
    if (!el) return;
    appInsights.trackEvent({
      name: "User_Click",
      properties: {
        elementType: el.tagName, text: (el.innerText || "").trim(),
        url: el.href || "", page: window.location.pathname,
        userId: userId, userName: userName, userEmail: userEmail,
        time: new Date().toISOString()
      }
    });
  });

  /* ── FORM SUBMIT ─────────────────────── */
  document.addEventListener("submit", function (e) {
    appInsights.trackEvent({
      name: "Form_Submit",
      properties: {
        formName: e.target.name || "unknown", page: window.location.pathname,
        userId: userId, userName: userName, userEmail: userEmail,
        time: new Date().toISOString()
      }
    });
  });

  /* ── INPUT FIELD TRACKING ────────────── */
  document.addEventListener("change", function (e) {
    var field = e.target;
    if (field.tagName === "INPUT" || field.tagName === "SELECT" || field.tagName === "TEXTAREA") {
      appInsights.trackEvent({
        name: "Field_Interaction",
        properties: {
          fieldName: field.name || field.id, page: window.location.pathname,
          userId: userId, userName: userName, userEmail: userEmail,
          time: new Date().toISOString()
        }
      });
    }
  });

  /* ── SCROLL TRACKING ─────────────────── */
  var scrollTracked = false;
  window.addEventListener("scroll", function () {
    if (!scrollTracked && window.scrollY > 300) {
      appInsights.trackEvent({
        name: "Scroll",
        properties: {
          scrollPosition: window.scrollY, page: window.location.pathname,
          userId: userId, userName: userName, userEmail: userEmail,
          time: new Date().toISOString()
        }
      });
      scrollTracked = true;
    }
  });

  /* ── TIME SPENT ──────────────────────── */
  var startTime = new Date();
  window.addEventListener("beforeunload", function () {
    var duration = (new Date() - startTime) / 1000;
    appInsights.trackEvent({
      name: "Time_Spent",
      properties: {
        seconds: duration, page: window.location.pathname,
        userId: userId, userName: userName, userEmail: userEmail
      }
    });
  });

  /* ── ERROR TRACKING ──────────────────── */
  window.onerror = function (message, source, lineno, colno, error) {
    appInsights.trackException({
      exception: error || new Error(message),
      properties: {
        source: source, line: lineno, column: colno,
        userId: userId, userName: userName, userEmail: userEmail
      }
    });
  };
</script>

🧠 Understanding the Code (Deep Explanation)

βœ” Initialization

Connects your portal to Azure Application Insights using the connection string and loads the SDK.

βœ” User Context

Captures logged-in user details using Power Pages Liquid variables (user.id, user.fullname, user.emailaddress1) and sets the authenticated user context.

βœ” Page Tracking

Tracks every page visited by the user by sending a trackPageView event with the page title and URL.


πŸ” Login Tracking

Tracks user login once per session using sessionStorage to prevent duplicate events on page refresh.


πŸšͺ Logout Tracking

Captures logout or tab close event using the browser's beforeunload event, giving accurate session end data.


πŸ–±οΈ Click Tracking

Tracks all clicks including menu items and buttons by bubbling up the DOM to find the nearest anchor or button element.


πŸ“ Form Tracking

Tracks form submissions with the form name and current page path.


⏱️ Time Spent Tracking

Calculates how long the user stays on a page in seconds by recording the start time and computing the difference on unload.


πŸ’Ύ Step 4: Save and Sync

After adding the script:

  1. 1 Click Save
  2. 2 Click Sync

πŸ”„ Step 5: Hard Refresh

Press Ctrl + Shift + R β€” this ensures the latest script loads and the browser cache is cleared.


πŸ§ͺ Step 6: Testing (Very Important)

βœ… Test Login

  1. 1Logout
  2. 2Login again and verify User_SignIn event appears in Azure

βœ… Test Clicks

  1. 1Click menu items
  2. 2Click buttons

βœ… Test Form Submit

Submit any form on the portal and verify the Form_Submit event is recorded.

βœ… Test Time Tracking

  1. 1Stay on a page for 20–30 seconds
  2. 2Navigate to another page

βœ… Test Logout

Click logout and verify User_SignOut is captured in Azure Logs.


πŸ“Š Step 7: Analyze Data in Azure

Navigate to: Azure β†’ Application Insights β†’ Logs

πŸ”Ή User Login Query

KQL
customEvents
| where name == "User_SignIn"
| project timestamp, userName, userEmail

πŸ”Ή User Activity Query

KQL
customEvents
| project timestamp, name, customDimensions
| order by timestamp desc

πŸ”Ή Time Spent Query

KQL
customEvents
| where name == "Time_Spent"
| summarize totalTime = sum(todouble(customDimensions.seconds))

πŸ”₯ What We Achieved

After implementing this solution, we successfully built:


πŸ’‘ Real-World Use Cases


πŸš€ Conclusion

By integrating Power Pages with Azure Application Insights, we can transform a simple portal into a powerful analytics platform.

Instead of guessing user behavior, we now have real data to make informed decisions.