PHPのエラーを Google Analytics で記録する

[GoogleアナリティクスでJavascriptエラー収集](http://qiita.com/hidek84/items/e42f8632d95b9444aea4) という投稿を見て、PHP（サーバーサイド）でもできるかも？と、軽い思いつきで作ってみた。

以前、ガラケーでもGoogle Analyticsに記録するとかをちょこっとやったことがあったので、それを応用した感じ。

まずは単体の関数。

```php
function exception_ga($errno, $msg, $file, $line){

	$types = array(
		E_ERROR => 'ERROR',
		E_WARNING => 'WARNING',
		E_PARSE => 'PARSE',
		E_NOTICE => 'NOTICE',
		E_CORE_ERROR => 'FATAL',
		E_CORE_WARNING => 'CORE_WARNING',
		E_COMPILE_ERROR => 'COMPILE_ERROR',
		E_COMPILE_WARNING => 'COMPILE_WARNING',
		E_USER_ERROR => 'USER_ERROR',
		E_USER_WARNING => 'USER_WARNING',
		E_USER_NOTICE => 'USER_NOTICE',
		E_STRICT => 'STRICT',
		E_RECOVERABLE_ERROR => 'RECOVERABLE_ERROR',
		E_DEPRECATED => 'DEPRECATED',
		E_USER_DEPRECATED => 'USER_DEPRECATED',
	);

	$fatal = false;
	if (
		$errno === E_ERROR
		|| $errno === E_CORE_ERROR
		|| $errno === E_COMPILE_ERROR
		|| $errno === E_RECOVERABLE_ERROR
	) {
		$fatal = true;
	}

	$description = $types[$errno].' - Line '.$line.': '.$msg;

	$url = 'https://www.google-analytics.com/collect';

	$data = array(
		'v' => 1,
		'ul' => 'ja',
		'de' => 'UTF-8',
		'tid' => 'UA-XXXXXXXX-X',
		'dh' => (!empty($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : php_uname('n')),
		'dl' => $file,
		'dr' => (!empty($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : null),
		'an' => 'PHP', // 本来はアプリ名用データ
		'av' => PHP_VERSION, // 本来はアプリバージョン用データ
		't' => 'exception',
		'exd' => $description,
		'exf' => $fatal,
	);

	// http://php.net/manual/ja/function.uniqid.php#94959
	$data['cid'] = sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
		mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
		mt_rand( 0, 0xffff ),
		mt_rand( 0, 0x0fff ) | 0x4000,
		mt_rand( 0, 0x3fff ) | 0x8000,
		mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
	);

	$ch = curl_init($url);
	curl_setopt($ch, CURLOPT_AUTOREFERER, true);
	if(!empty($_SERVER['HTTP_USER_AGENT'])){
		curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
	}
	curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded'));
	curl_setopt($ch, CURLOPT_POST, true);
	curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($ch, CURLOPT_HEADER, false);
	$res = curl_exec($ch);
	curl_close($ch);
	var_dump($data);
}
```

それで、上記の関数を `set_error_handler` とかで呼び出して情報を渡す。

```php
// 普通のエラーのとき
set_error_handler(function ($errno, $msg, $file, $line) {
	if (!(error_reporting() & $errno)) {
		return;
	}

	exception_ga($errno, $msg, $file, $line);
});

// Fatalエラーとかも取るとき？（やってみたけど動かなかった）
register_shutdown_function(function() {
	if ($error = error_get_last()) {
		exception_ga($error['type'], $error['message'], $error['file'], $error['line']);
	}
});
```

Google Analyticsの[例外トラッキング](https://developers.google.com/analytics/devguides/collection/analyticsjs/exceptions?hl=ja)を使ってるけど、少し特殊な設定として「アプリ名（キー名が`an`）」に「PHP」を設定してる。
（念のためアプリバージョン `av` でPHPのバージョンも設定してる）

こうしておくと、レポートでこういう感じに出る。

![2015-02-24_235230.png](https://qiita-image-store.s3.amazonaws.com/0/37122/0c4ef3e4-9fa4-4c62-2dc9-7b1555a1fbfa.png)

JavaScriptでもエラーを収集してたりすると、分かれてた方がいいかと思って。

ちなみに例外トラッキングは現時点ではカスタムレポートでないと見れないので、自分で作る必要がある。
好みによるけど、とりあえず以下のように作った。

![2015-02-24_235625.png](https://qiita-image-store.s3.amazonaws.com/0/37122/37dd377a-6f80-9655-5d15-2d4a523b31a4.png)

アプリ名が最上層で、次にページ（エラーが起こったファイル）単位、その後にエラー内容が見られる形にしてる。
こんな感じで日本語のメッセージも問題なさそうだった。

![2015-02-25_000506.png](https://qiita-image-store.s3.amazonaws.com/0/37122/a0c43e47-da1a-2cc1-1ec1-70ecc7b17b29.png)


表中の「クラッシュ」というのは関数内の「`$fatal`」で設定してあり、致命的なエラーの場合にフラグが立つようにしてるけど、致命的なのでそもそも`curl_exec()`まで実行されないっぽい・・・？

-----

勢いというか思いつきで書いたので、 `curl_multi_exec()` とか使ってもっと丁寧に作る必要がありそう。