Shukry Zablah

2021-03-02

In this post I comment on how I added Google Analytics tracking to this website. In my earlier posts I mentioned how I'm using Common Lisp tooling, specifically, the Coleslaw blogware. Coleslaw actually already had a Google Analytics plugin, but unfortunately it used legacy code (the javascript not the lisp) and I did not see any reason to include this in my personal site. This is why I added what I wanted to Coleslaw.

How do injections work?

From the plugin-api docs we get:

The injection is a function that takes a Document and returns a string to insert in the page or nil. The keyword specifies whether the injected text goes in the HEAD or BODY element.

It basically allows us to input whatever we want into the head or the body of the html that gets generated by Coleslaw.

I wanted to know how the plugins get called. I won't go into much detail but just enough to point the next person in the right direction.

The plugins are simple files, with a very specific name. I knew that the answer was not in them, so I searched the whole directory (project regex search is my favorite command), and found that the function enable-plugin is responsible for calling the enable function in the plugin with the injection. It picks up all the files that start with coleslaw- prefix. In summary, load-config calls load-plugins which call enable-plugin for all the plugins.

The gtag plugin

Building the gtag plugin was simpler than it sounds. It is largely the same code than the analytics plugin. The injection is new though. I have included the code for the whole plugin here below:

(defpackage :coleslaw-gtag
  (:use :cl)
  (:export #:enable)
  (:import-from :coleslaw #:add-injection))

(in-package :coleslaw-gtag)

(defvar *analytics-js*
"<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src='https://www.googletagmanager.com/gtag/js?id=~a'></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', '~a');
</script>")

(defun enable (&key tracking-code)
  (let ((snippet (format nil *analytics-js* tracking-code tracking-code)))
    (add-injection (constantly snippet) :head)))

The code above is defining the package first, and then defining the code injection. Note that the only part responsible for adding the injection is the short body of the enable method.

Conclusion

Google Analytics is an amazing tool. I already submited a PR to add this gtag plugin. For now I am using my custom Coleslaw fork with my two changes. I hope they get a response from the maintainers sometime.