Googleアナリティクスのトラッカー生成処理を徹底解説

## はじめに

Google Analytics Advent Calender 12日目の記事です。

この記事では、普段Googleアナリティクスのコードを入れている時に、何気なく書いている

```js
ga('create', 'UA-111111-22');
```

という、たった1行のトラッカー生成コマンドの裏で、どんな処理が行われているかを紹介します。

内容はマニアックなであり、かつ知っていても得するシーンはほとんどないので、ご注意ください。

GoogleアナリティクスのJavaScriptコードは、
[http://www.google-analytics.com/analytics.js](http://www.google-analytics.com/analytics.js)
にあるものが全てです。このソースコードの中から、トラッカーを生成している箇所がどのようになっているのか？を説明していきます。なお、上記コードは変数名が短縮化され、余分な改行やスペースなどはすべて削除されたminifyコードです。改行・スペースなどを入れて整形するには、[SEM Technology - ソースコード整形ツール](https://sem-technology.info/tools/beautifier)などを使うといいと思います。ただ、変数名は短縮されたままなので、慣れるまではコードリーディングに時間がかかります。

## トラッカーを生成している箇所を確認
```N.create=``` というキーワードで検索してみてください。該当箇所がトラッカーを生成する関数を定義している箇所になります。
読みやすいように、整形したものを下に記載します。

```js
N.create = function(a) {
  var b = za(uc, [].slice.call(arguments));
  b[V] || (b[V] = "t0");
  var c = "" + b[V];
  if (N.h[c])
    return N.h[c];
  b = new pc(b);
  N.h[c] = b;
  N.P.push(b);
  return b
}
```

大まかには、

- 最初に引数をパースする
- 前処理を行って、すでにトラッカー生成済みならそのトラッカーを返す
- トラッカーがなければトラッカーを作成する
- 生成したトラッカーを登録
- 生成したトラッカーを返す

といった処理をしています。では、それぞれの処理をもう少し細かく見てみます。

## 各変数/関数の処理について
| No | 変数/関数名 | 役割 |
|:--:|:---------:|:----|
| 1 | a | createコマンドの第2引数(トラッキングID)を保持 |
| 2 | za | createコマンドの引数をパースする関数。 |
| 3 | uc | パース時のオプション。パース後のハッシュのキーのデフォルト値を設定。 |
| 4 | b | パース処理が完了した引数情報。ハッシュObject。 |
| 5 | V | 'name'という文字列。トラッカー名を表すパラメーター |
| 6 | c | 生成するトラッカー名。createコマンドでトラッカー名が指定されていればそのトラッカー名。指定されていない場合はデフォルトのトラッカー名である「t0」が使われる |
| 7 | N.h | トラッカー名からトラッカーオブジェクトを取得するハッシュ。 |
| 8 | pc | トラッカー本体を生成するコンストラクタ関数 |
| 9 | N.P | 生成されているトラッカーをすべて保持する配列 |

### za関数/uc変数の詳細
za関数の動きが少し分かり難いはずです。実際の例を出すと、下記のようになります。

```js
ga('create', 'param1', 'param2', 'param3', {
  'obj-param1': 'obj-value1',
  'obj-param2': 'obj-value2'
});

// 上のcreate文に対して、za関数を下記のように呼び出すと
za(['key1', 'key2', 'key3', 'otherName'], [].slice.call(arguments));

// こうなります。
b = {
  'key1': 'param1',
  'key2': 'param2',
  'key3': 'param3',
  'obj-param1': 'obj-value1',
  'obj-param2': 'obj-value2'
}
```

ソースコード上、za関数の第1引数には、 

```js
[ 'trackingId', 'cookieDomain', 'name' ]
```

と指定されているので、

```js
ga('create', 'UA-123456-7', { 'name': 'myTrackerName' })
```

と

```js
ga('create', 'UA-123456-7', 'myTrackerName');
```

は、同じ処理になります（実際、ヘルプではどちらの書き方も記載されています）。

## トラッカー名を指定せず、異なるトラッキングIDのcreateを2回行ったらどうなる？

同一ページにGoogleアナリティクスを複数設置する際は、**マルチトラッキング**という、カスタマイズを行う必要があります。カスタマイズ内容は単純で、それぞれのトラッカーを生成するときに、区別できるように名前をつけてあげましょう、という点と、各処理を呼び出すときに、どのトラッカーに対する処理なのかをわかるように名前をつけて呼び出しましょう、という内容です。
具体的には、

```js
ga('create', 'UA-123456-7', 'auto', { 'name': '1stTracker' });
ga('create', 'UA-123456-8', 'auto', { 'name': '2ndTracker' });
ga('1stTracker.send', 'pageview');
ga('2ndTracker.send', 'pageview');
```

といったコードになります。では、このトラッカー名を付け忘れたら、どうなるのでしょうか？つまり、

```js
ga('create', 'UA-123456-7', 'auto');
ga('create', 'UA-123456-8', 'auto');
ga('send', 'pageview');
ga('send', 'pageview');
```

のような形です。
では、N.create関数の中の動きを見ていきましょう。

## 1つ目のcreate

```js
var b = za(uc, [].slice.call(arguments));
```

の実行で、bには

```js
{
  'trackingId': 'UA-123456-7',
  'cookieDomain': 'auto',
}
```

といったオブジェクトが代入されます。次に、

```js
b[V] || (b[V] = "t0");
```
の処理により(Vは"name")、 ```b['name'] = 't0'``` が定義されます。
次の

```js
var c = "" + b[V];
```

では、単に変数「t0」を念のため文字列化しているだけでしょう。そして、

```js
  if (N.h[c])
    return N.h[c];
```

によって、該当のトラッカー名「t0」がすでに生成済みであるかどうかを確認し、生成済みであれば、そのトラッカーをreturnしています。ここでは、まだ未生成のためifの中には入らずに、次の行に進みます。

```js
b = new pc(b);
N.h[c] = b;
N.P.push(b);
return b
```

一気に進めますが、トラッカーを生成して、生成済みトラッカーの一覧に登録し、生成されたトラッカーをreturnして完了です。ここまでは問題なく実行できています。

### 2つ目のcreate

```js
  var b = za(uc, [].slice.call(arguments));
  b[V] || (b[V] = "t0");
  var c = "" + b[V];
```

の処理は1つ目の時と同様です。cには1つ目と同様に「t0」がセットされています。

この状態で、

```js
  if (N.h[c])
    return N.h[c];
```

を評価するわけですが、1つ目のトラッカーを生成した時に、 ```N.h['t0']``` はすでに定義済みとなります。そのため、このif文の条件がtrueと評価され、1つ目で生成したトラッカーをreturnします。

そのため、2つ目のトラッキングIDを持ったトラッカーは生成されることなく、不具合のある計測コードとなってしまいます。


## まとめ
今回は、Googleアナリティクス導入の第1歩目にあたるトラッカー生成コマンドの内部処理を紹介しました。

他にもanalytics.jsを読んでいると、気付くことはいろいろあります。例えば、

- クロスドメイントラッキングで付与されるパラメーター名「_ga」は変更することはできない
- 送信データが2036バイト以下の場合、GETリクエストとなり、2037バイト以上、8192バイト以下の場合、POSTリクエストとなる。8193バイト以上の場合、例外がthrowされ、データ計測は行われない

などを知ることができます。興味のある方は是非、今日からanalytics.jsを読んでみてください。