Tracking website usage is essential for understanding your audience and improving your content. For my Hugo website, I chose to use Azure Application Insights (App Insights) because I just like it.

This guide is inspired by this tutorial.

Setting Up Azure Resources

To get started, search for Application Insights in the Azure Marketplace.

marketplace

Click Create and fill in the required details:

details

Once done, click Review + Create to finalize the setup.

Basic Configuration

After creating your App Insights instance, locate the Instrumentation Key. This key is required for sending metrics to App Insights.

Add the key to your hugo.yaml or hugo.config file like this:

params:
  azureAppInsightsKey: "<your-instrumentation-key>"

Storing the key here makes it easy to update or switch to a different analytics provider in the future. I also use this file to configure other settings, like enabling LaTeX support with math: true.

Updating the Template

To track visits on every page, you need to inject the App Insights script into all pages. This can be done by extending the base template of your website.

In my setup, I have an extend_head.html file under layouts/partials. Here’s the structure:

layouts
|-- partials
    |-- extend_head.html    // Extends the head property
    |-- footer.html         // Custom footer
    |-- math.html           // LaTeX support

In extend_head.html, I added the following line to include the App Insights script:

{{ if or .Params.math .Site.Params.math }} {{ partial "math.html" . }} {{ end }}
{{ partial "appinsights.html" . }}
<!-- Added this line to include App Insights template -->

Next, create an appinsights.html file under the same layouts/partials folder with the following content:

{{ if .Site.Params.azureAppInsightsKey }}
<script type="text/javascript">
  !(function (T, l, y) {
    var S = T.location,
      u = "script",
      k = "instrumentationKey",
      D = "ingestionendpoint",
      C = "disableExceptionTracking",
      E = "ai.device.",
      I = "toLowerCase",
      b = "crossOrigin",
      w = "POST",
      e = "appInsightsSDK",
      t = y.name || "appInsights";
    (y.name || T[e]) && (T[e] = t);
    var n =
      T[t] ||
      (function (d) {
        var g = !1,
          f = !1,
          m = { initialize: !0, queue: [], sv: "4", version: 2, config: d };
        function v(e, t) {
          var n = {},
            a = "Browser";
          return (
            (n[E + "id"] = a[I]()),
            (n[E + "type"] = a),
            (n["ai.operation.name"] = (S && S.pathname) || "_unknown_"),
            (n["ai.internal.sdkVersion"] =
              "javascript:snippet_" + (m.sv || m.version)),
            {
              time: (function () {
                var e = new Date();
                function t(e) {
                  var t = "" + e;
                  return 1 === t.length && (t = "0" + t), t;
                }
                return (
                  e.getUTCFullYear() +
                  "-" +
                  t(1 + e.getUTCMonth()) +
                  "-" +
                  t(e.getUTCDate()) +
                  "T" +
                  t(e.getUTCHours()) +
                  ":" +
                  t(e.getUTCMinutes()) +
                  ":" +
                  t(e.getUTCSeconds()) +
                  "." +
                  ((e.getUTCMilliseconds() / 1e3).toFixed(3) + "").slice(2, 5) +
                  "Z"
                );
              })(),
              iKey: e,
              name:
                "Microsoft.ApplicationInsights." +
                e.replace(/-/g, "") +
                "." +
                t,
              sampleRate: 100,
              tags: n,
              data: { baseData: { ver: 2 } },
            }
          );
        }
        var h = d.url || y.src;
        if (h) {
          function a(e) {
            var t, n, a, i, r, o, s, c, p, l, u;
            (g = !0),
              (m.queue = []),
              f ||
                ((f = !0),
                (t = h),
                (s = (function () {
                  var e = {},
                    t = d.connectionString;
                  if (t)
                    for (var n = t.split(";"), a = 0; a < n.length; a++) {
                      var i = n[a].split("=");
                      2 === i.length && (e[i[0][I]()] = i[1]);
                    }
                  if (!e[D]) {
                    var r = e.endpointsuffix,
                      o = r ? e.location : null;
                    e[D] =
                      "https://" +
                      (o ? o + "." : "") +
                      "dc." +
                      (r || "services.visualstudio.com");
                  }
                  return e;
                })()),
                (c = s[k] || d[k] || ""),
                (p = s[D]),
                (l = p ? p + "/v2/track" : config.endpointUrl),
                (u = []).push(
                  ((n =
                    "SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details)"),
                  (a = t),
                  (i = l),
                  ((o = (r = v(c, "Exception")).data).baseType =
                    "ExceptionData"),
                  (o.baseData.exceptions = [
                    {
                      typeName: "SDKLoadFailed",
                      message: n.replace(/\./g, "-"),
                      hasFullStack: !1,
                      stack:
                        n +
                        "\nSnippet failed to load [" +
                        a +
                        "] -- Telemetry is disabled\nHelp Link: https://go.microsoft.com/fwlink/?linkid=2128109\nHost: " +
                        ((S && S.pathname) || "_unknown_") +
                        "\nEndpoint: " +
                        i,
                      parsedStack: [],
                    },
                  ]),
                  r)
                ),
                u.push(
                  (function (e, t, n, a) {
                    var i = v(c, "Message"),
                      r = i.data;
                    r.baseType = "MessageData";
                    var o = r.baseData;
                    return (
                      (o.message =
                        'AI (Internal): 99 message:"' +
                        (
                          "SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details) (" +
                          n +
                          ")"
                        ).replace(/\"/g, "") +
                        '"'),
                      (o.properties = { endpoint: a }),
                      i
                    );
                  })(0, 0, t, l)
                ),
                (function (e, t) {
                  if (JSON) {
                    var n = T.fetch;
                    if (n && !y.useXhr)
                      n(t, {
                        method: w,
                        body: JSON.stringify(e),
                        mode: "cors",
                      });
                    else if (XMLHttpRequest) {
                      var a = new XMLHttpRequest();
                      a.open(w, t),
                        a.setRequestHeader("Content-type", "application/json"),
                        a.send(JSON.stringify(e));
                    }
                  }
                })(u, l));
          }
          function i(e, t) {
            f ||
              setTimeout(function () {
                (!t && m.core) || a();
              }, 500);
          }
          var e = (function () {
            var n = l.createElement(u);
            n.src = h;
            var e = y[b];
            return (
              (!e && "" !== e) || "undefined" == n[b] || (n[b] = e),
              (n.onload = i),
              (n.onerror = a),
              (n.onreadystatechange = function (e, t) {
                ("loaded" !== n.readyState && "complete" !== n.readyState) ||
                  i(0, t);
              }),
              n
            );
          })();
          y.ld < 0
            ? l.getElementsByTagName("head")[0].appendChild(e)
            : setTimeout(function () {
                l.getElementsByTagName(u)[0].parentNode.appendChild(e);
              }, y.ld || 0);
        }
        try {
          m.cookie = l.cookie;
        } catch (p) {}
        function t(e) {
          for (; e.length; )
            !(function (t) {
              m[t] = function () {
                var e = arguments;
                g ||
                  m.queue.push(function () {
                    m[t].apply(m, e);
                  });
              };
            })(e.pop());
        }
        var n = "track",
          r = "TrackPage",
          o = "TrackEvent";
        t([
          n + "Event",
          n + "PageView",
          n + "Exception",
          n + "Trace",
          n + "DependencyData",
          n + "Metric",
          n + "PageViewPerformance",
          "start" + r,
          "stop" + r,
          "start" + o,
          "stop" + o,
          "addTelemetryInitializer",
          "setAuthenticatedUserContext",
          "clearAuthenticatedUserContext",
          "flush",
        ]),
          (m.SeverityLevel = {
            Verbose: 0,
            Information: 1,
            Warning: 2,
            Error: 3,
            Critical: 4,
          });
        var s = (d.extensionConfig || {}).ApplicationInsightsAnalytics || {};
        if (!0 !== d[C] && !0 !== s[C]) {
          (method = "onerror"), t(["_" + method]);
          var c = T[method];
          (T[method] = function (e, t, n, a, i) {
            var r = c && c(e, t, n, a, i);
            return (
              !0 !== r &&
                m["_" + method]({
                  message: e,
                  url: t,
                  lineNumber: n,
                  columnNumber: a,
                  error: i,
                }),
              r
            );
          }),
            (d.autoExceptionInstrumented = !0);
        }
        return m;
      })(y.cfg);
    (T[t] = n).queue && 0 === n.queue.length && n.trackPageView({});
  })(window, document, {
    src: "https://az416426.vo.msecnd.net/scripts/b/ai.2.min.js", // The SDK URL Source
    //name: "appInsights", // Global SDK Instance name defaults to "appInsights" when not supplied
    //ld: 0, // Defines the load delay (in ms) before attempting to load the sdk. -1 = block page load and add to head. (default) = 0ms load after timeout,
    //useXhr: 1, // Use XHR instead of fetch to report failures (if available),
    //crossOrigin: "anonymous", // When supplied this will add the provided value as the cross origin attribute on the script tag
    cfg: {
      // Application Insights Configuration
      instrumentationKey: "{{- .Site.Params.azureAppInsightsKey -}}",
      /* ...Other Configuration Options... */
    },
  });
</script>
{{ end }}

Note:
The script content above all come from this blog

With these changes, your Hugo website will now send traffic data to Azure Application Insights.