Некоторые ошибки происходят на стороне пользователя и не попадают в логи на сервере. В этом могут быть виноваты настройки сети, плагины браузера, которые установил пользователь, политики безопасности или антивирус, которые блокируют загрузку некоторых файлов.
О наличии такой проблемы сложно узнать, т.к. обычный пользователь едва ли станет тратить своё время, чтобы сообщить о ней, а в логах на сервере вы их просто не увидите.
Предлагаемый подход основан на обработке в js ошибок объекта браузера window и отправке этих данных на сервер.
Пусть скрипт на сервере находится по адресу: /front-end/logger. Он читает GET параметры и где то их сохраняет. А пока нужно выполнить перехват и обработку ошибки на front-end:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
try { // функция отправки данных на сервер function pinger(data) { let img = new Image(); img.src = "/front-end/logger" + encodeQueryData(data); } // вспомогательная функция формирования строки запроса function encodeQueryData(data) { const ret = []; for (let d in data) ret.push(encodeURIComponent(d) + '=' + encodeURIComponent(data[d])); return ret.length ? '?' + ret.join('&') : ''; } // добавляем обработчик события ERROR window.addEventListener('error', (e) => { let tagName = e.srcElement.tagName; let data = { type: tagName, page: window.location.href, userAgent: navigator.userAgent, platform: navigator.platform, }; // вычисляем проблемный URL if (tagName == 'LINK') { data.url = e.srcElement.href; } if (tagName == 'IMG') { data.url = e.srcElement.src; } if (data.url !== undefined) { // попробуем выявить http ошибки и текст сообщения fetch(data.url).then(function(response) { data.status = response.status; data.statusText = response.statusText; pinger(data); }).catch((error) => { data.status = 'FETCH ERROR'; data.statusText = error; pinger(data); }); } }, true); } catch (e) { ; } |
Скрипт собирает следующий пакет данных:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[ // тип контейнера или тег (LINK / IMG) type, // url, где произошла ошибка page: // имя браузера (из объекта navigator) userAgent, // платформа (например, win32) platform, // url файла, который не был загружен url, // http код ошибки или 'FETCH ERROR' status, // текст ошибки statusText ] |
Если удаётся получить http код ошибки, то скорее всего вы найдете её и в логах на веб-сервере (либо в логах одного из прокси или кеширующих серверов). Если же вместо http кода вы получаете ‘FETCH ERROR’, то файл был блокирован до запроса на сервер и надо разбираться почему это происходит со стороны пользователя.
Ошибки типа LINK — это ошибки при загрузке CSS/JS файлов. Они могут сильно влиять на работу сайта, им нужно уделить пристальное внимание.
Back-end часть logger-а для Drupal
Придумать куда писать логи на back-end, вам не составит труда для вашего частного случая. Но у меня есть готовый вариант для cms drupal 7, и я им здесь поделюсь.
Одна функция объявляет end-point для вызова логирования, а вторая функция — запись лога в системный лог cms.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
/** * Implementation hook user login. */ function MYMODULE_menu() { $items = array(); $items['front-end/logger'] = array( 'page callback' => '_mymodule_log_error', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); return $items; } /** * Callback endpoint front-end/logger */ function _mymodule_log_error() { $inputVars = ['type', 'page', 'url', 'userAgent', 'platform', 'status', 'statusText']; $data = []; foreach ($inputVars as $varName) { $data[$varName] = empty($_GET[$varName]) ? '' : $_GET[$varName]; } if (in_array($data['type'], ['IMG', 'LINK'])) { $message = "Error on {$data['page']}, {$data['status']}: {$data['statusText']}, file: {$data['url']}. {$data['platform']}. {$data['userAgent']}. "; watchdog('FRONTEND', $message, NULL, WATCHDOG_ALERT, $data['page']); } exit; } |