Google AnalyticsのCookieの仕組みを解説

## はじめに
Google Analytics Advent Calender 17日目の記事です。

この記事では、Googleアナリティクスの非常にマニアックな領域について解説しています。

Google Analyticsでは、ユーザーを特定するために、ファーストパーティーのCookieを用いています。デフォルトでは、_gaという変数名のCookieに、クライアントIDと呼ばれるユーザー特定用の文字列にさらにプラスアルファの文字列が設定されており、2年間の有効期限になっています。

下記が設定される一般的なCookieの例になります。

> GA1.1.1333214135.1439909622

このように、Cookieの値は、ドットで区切られた4つのコンポーネントで構成されています。このうち後ろ2つのコンポーネントが一般にクライアントIDと呼ばれているGoogleアナリティクスのプラットフォームにおいてユーザーを一意に特定するために使われている文字列になります。
また、前2つはユーザーを特定するためには使われておらず、同一ユーザーであっても将来変化する可能性がある項目になります。


## 各コンポーネントについて
ここでは、Googleアナリティクスに置いてユーザーを特定するために用いられるCookie「_ga」(Cookie NameはGoogleアナリティクスの設定で変更可能)の完全なフォーマットを使って説明します。

> GA1.1-3.1333214135.1439909622

において、

| 値 | 説明 |
|:---|:----|
| GA1 | バージョン番号。将来Cookieの採番方式などが変わった時に、バージョン番号が上がり、旧バージョンからアップグレードされるはず。 |
| 1 | このCookieが紐付いているドメインに関する情報 |
| 3 | このCookieが紐づいているパスに関する情報 |
| 1333214135 | ランダムな数字の文字列 |
| 1439909622 | 初回訪問のタイムスタンプ |

### バージョン番号
このバージョン番号はGoogleアナリティクスのバージョンというよりも、「GoogleアナリティクスのCookie仕様のバージョン」のことかと思います。将来的にCookieの構成が変わったりした時にも、このバージョン番号から当時の仕様でCookie値を読み取って、最新の仕様に変換することができるように保持しているものと思われます。

### ドメインとパスに関する情報
ここが一番難しいと思います。自分もここに何が入っているのか分からず、さらにいろいろ調べても出てこないので、[GoogleアナリティクスのJavaScriptソースコード](http://www.google-analytics.com/analytics.js)と長時間格闘しました。

普段よくみる2つ目のコンポーネントは、数字が1つ（おそらく「1」のものが多いと思います）ですが、実際のところは、2つの数字をハイフンでつないだ文字列になります。1つ目がCookieが紐づくドメイン(cookieDomain)に関する情報で2つ目がCookieが紐づくページパスに関する情報になります。そして、2つ目のCookieが紐づくページパス（cookiePath）が指定されていないケースではここが省略されて、単一の数字のみになる、という仕様になっています。

#### cookieDomainに関する情報
ソースコード上では、

```js
lc = function(a) {
  return 0 == a.indexOf(".") ? a.substr(1) : a
}

ic = function(a) {
  return lc(a).split(".").length
}
```

といった実装になっており、引数aは、

- トラッカー生成時にcookieDomainを指定していれば、そのドメイン
- cookieDomainを「auto」に設定していれば「auto」
- 上記以外の場合は、「window.location.hostname」で得られるホスト名

がセットされてきます。そして、それらを「.」（ドット）で区切った時の要素数が設定されます。

#### cookiePathに関する情報
こちらのソースコードは、

```js
kc = function(a) {
  if (!a)
    return "/";
  1 < a.length && a.lastIndexOf("/") == a.length - 1 && (a = a.substr(0, a.length - 1));
  0 != a.indexOf("/") && (a = "/" + a);
  return a
},
jc = function(a) {
  a = kc(a);
  return "/" == a ? 1 : a.split("/").length
}
```

のようになっています。見た目は少し複雑ですが、cookieDomainとほぼ同様の処理が行われています。こちらはドットで区切るのではなく、URLパスの区切り文字であるスラッシュで区切った要素数を返します。

#### 後処理
あとは、cookieDomainから取得した情報とcookiePathから取得した情報をハイフンで接続します。この時に、cookiePathから取得した情報が「0」である場合（cookiePathを指定しなければ0となる）、cookiePathから取得した情報は省略され、普段よくみる_gaのcookieの値になります。


### ランダムな数字の文字列
ここはランダムな数字列を作り出しています。ランダムな数字列の生成には、

- navigator.userAgent
- document.cookie
- document.referrer
- window.history.length
- Math.random()で取得した乱数

を使って生成しているようです。Math.random()だけでなく、ユーザーエージェントやCookie、リファラー、ブラウザ履歴の数なども使ってランダムな数列を生成しているあたり、意図せぬ重複をできるだけ排除できるように実装していることが伺えます。

### 初回訪問のタイムスタンプ
最後のコンポーネントは、

```js
Math.round((new Date).getTime() / 1E3)
```

のように生成されているので、非常に分かりやすいです。なので、初回訪問日時が知りたい場合は、このコードから逆算して、

```js
ga(function(tracker) {
  var timestamp = tracker.get('clientId').split('.')[1];
  var firstVisitDatetime = new Date(timestamp * 1E3);
  console.log(firstVisitDatetime);
});
```

のように取得できそうです。

## まとめ
このようなGoogleアナリティクスのcookieの細かな仕様を知らなくても、Googleアナリティクスを普通に使う分には全く困りません。しかし、トラッキングコードの高度なカスタマイズを実施する場合にこういったことを知っているか知らないかで大きな差が出てくることがあります。そんなわけで、皆さんもGoogleアナリティクスのトラッキングコードのソースコード・リーディングをしてみてはいかがでしょうか。