Логирование js-ошибок в piwik (аналог google analytics)

Piwik — аналог google analytics, который ставится на свой домен. Копирует апи google analytics, так что всё описанное можно с небольшими изменениями применить и при использовании google analytics.

Логирование

Ошибки записываются и в пивик, и в html-элемент на странице (удобно для автоматического тестирования). Код содержится в файле `error_logger.js`:

(function () {
	"use strict";

	var logToHtmlId = window.app_config && window.app_config.log_errors_to_html_id,
		logToPiwikPermitted = window.app_config && window.app_config.log_errors_to_piwik,
		logHtmlEl;

	if (window.app_config && window.app_config.debug) {
		return;
	}

	/**
	 * Log error to some html element
	 */
	function logToHtml(params)
	{
		if (! logToHtmlId) {
			return;
		}
		if (! logHtmlEl) {
			logHtmlEl = document.getElementById(logToHtmlId);
		}

		if (logHtmlEl) {
			document.getElementById(logToHtmlId).innerHTML += JSON.stringify(params);
		}
	}

	/**
	 * Log error to piwik
	 */
	function logToPiwik(params)
	{
		if (! logToPiwikPermitted) {
			return;
		}

		if (! window._paq) {
			window._paq = [];
		}
		window._paq.push(['trackEvent', params.type, params.msg, JSON.stringify(params)]);
	}

	window.logError = function logError(params) {
		params = {
			type: params.type || ' ', // not empty string because this param is not optional for piwik and it does not want empty field
			msg: params.msg || ' ', // the same as for type
			stack: params.stack || null,
			ua: navigator && navigator.userAgent,
			url: params.url || '',
			href: window.location.href,
			line: params.line || null
		};
		logToHtml(params);
		logToPiwik(params);
	};

	window.onerror = function(msg, url, line)
	{
		var preventErrorAlert = true;

		var err = {msg: msg, url: url, line: line, type: 'unhandled-onerror'};
		window.logError(err);

		return preventErrorAlert;
	};


	window.onload = function()
	{
		if (! window.__angularLoaded) {
			window.logError({type: 'app-not-started'});
		}
	};
}());

Здесь, помимо объявления глобальной функции для логирования, есть ещё два важных момента:
`window.onerror` логирует все неперехваченные js-ошибки;
`window.onload` проверяет установку глобальной переменной `window.__angularLoaded`, которая ставится внутри AngularJS-приложения, и в случае необходимости логирует факт незапустившегося приложения.

Использование внутри AngularJS

Осталось научиться записывать ошибки внутри AngularJS-приложения. В этом нам поможет Бен Надель — http://www.bennadel.com/blog/2542-logging-client-side-errors-with-angularjs-and-stacktrace-js.htm.
В итоге сервис логирования выглядит так:

(function () {
	"use strict";
	/**
	 * @desc Service for logging angular errors to external services
	 * @link http://www.bennadel.com/blog/2542-logging-client-side-errors-with-angularjs-and-stacktrace-js.htm
	 */
	var module = angular.module('Utils.UtilsNgErrorLogger', [])
		.factory('UtilsNgErrorLogger', UtilsNgErrorLogger);

	UtilsNgErrorLogger.$inject = ['$log'];

	function UtilsNgErrorLogger($log)
	{
		function log(exception, cause)
		{
			$log.error.apply( $log, arguments );
			exception = exception || {};
			cause = cause || '';
			var msg = (exception.message || '') + '(caused by ' + cause + ')';

			if (window.logError)
			{
				window.logError({
					type: 'ng-error',
					msg: msg,
					stack: exception.stack || null
				});
			}
		}
		return log;
	}
}());

Как при этом выглядит index.html

<!doctype html>
<html>
<head>
   <link rel="stylesheet" href="/css/libs.css">
   <link rel="stylesheet" href="/css/app.css">
   <script type="text/javascript" src="/conf.js"></script>
   <script type="text/javascript" src="/js/error_logger.js"></script>
   <script type="text/js-sm-errors" id="js-error-id"></script>
</head>
<body
      ng-app="MyApp">

<!-- html-код приложения -->

<script type="text/javascript" src="/js/libs.js"></script>
<script type="text/javascript" src="/js/views.js"></script>
<script type="text/javascript" src="/js/app.js"></script>
<script type="text/javascript">
var _paq = _paq || [];
_paq.push(["setDocumentTitle", document.domain + "/" + document.title]);
_paq.push(["setCookieDomain", "*.<domain>"]);
_paq.push(["setDomains", ["*.<domain>"]]);
_paq.push(["trackPageView"]);
_paq.push(["enableLinkTracking"]);

(function() {
   var u=(("https:" == document.location.protocol) ? "https" : "http") + "://<piwik_host>/";
   _paq.push(["setTrackerUrl", u+"piwik.php"]);
   _paq.push(["setSiteId", "1"]);
   var d=document, g=d.createElement("script"), s=d.getElementsByTagName("script")[0]; g.type="text/javascript";
   g.defer=true; g.async=true; g.src=u+"piwik.js"; s.parentNode.insertBefore(g,s);
})();
</script>
</body>
</html>

Пояснения

Нужно заменить `<domain>` на свой домен, а `<piwik_host>` — на урл до сервера с пивиком.
Сюда пишутся ошибки для удобства автоматического тестирования:

<script type="text/js-sm-errors" id="js-error-id"></script>

Здесь подгружаются конфиги для разрешения логирования в пивик и указания id элемента для записи ошибок:

<script type="text/javascript" src="/conf.js"></script>

Например:

var app_config = {
	log_errors_to_html_id: 'js-error-id',
	log_errors_to_piwik: true
};

Ещё можно логировать время загрузки страницы пользователями

https://blog.bullgare.com/2015/05/%D0%BB%D0%BE%D0%B3%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%B2%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%B8-%D0%B7%D0%B0%D0%B3%D1%80%D1%83%D0%B7%D0%BA%D0%B8-%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8/

Альтернативы

Ещё одно описание системы логирования js-ошибок в google analytics — http://blog.gospodarets.com/track_javascript_angularjs_and_jquery_errors_with_google_analytics/

LEAVE A COMMENT