Логування помилок PHP. Логування у PHP за допомогою Zend Log. Скрипт php для створення лог-файлу

У кожному реальному PHPдодатків іноді виникають помилки і винятки, вискакують попередження, повідомлення. Якщо ми не будемо записувати цю інформацію (логувати), то одного разу стане неможливим зрозуміти, в якій частині програми виникають ці помилки і винятки, і, відповідно, не зможемо вирішити їх. До того ж, існують такі ситуації, коли логування подій, дій просто необхідне, як у випадку з входом користувача в систему і виходом з неї, наприклад.

У PHP вже існують необхідні засоби для журналування: функція error_log()– для надсилання повідомлення до системного журналу, функція set_error_handler(), для перехоплення попереджень та помилок. Ці функції можуть бути використані для управління управління помилками, даючи розробнику коду можливість самостійного управління логікою обробки і фільтрації помилок.

Однак такий низькорівневий доступ призводить до частого дублювання коду, і що ще важливіше, такий код більш схильний до помилок. Тому на допомогу програмісту приходять вже готові компоненти, які добре протестовані і зарекомендували себе в “бойових” умовах.

До таких компонентів належить компонент zend-logз фреймворку Zend. Компонент zend-logможе бути використаний як багатоцільовий компонент логування, такий майстер на всі руки. Він підтримує безліч форматів журнальних повідомлень і різновидів баз логування (файли, бази даних), плюс має пропрацьовану систему фільтрації повідомлень і багато чого ще. Також zend-logсумісний з PSR-3стандартом логування. Встановлюється так:

Composer require zendframework/zend-log

Використовується так (для прикладу використовується файл index.php в корені проекту):

Потрібно "vendor/autoload.php";

Use Zend\Log\Logger;
use Zend\Log\Writer\Stream;

$logger = новий Logger;

// надсилаємо помилки в консоль
$writer = новий Stream("php://ouput");

$logger -> addWriter($writer);
$logger -> log(Logger::INFO,"Якась інформація");

Результат виконання коду вище:

2017-09-26T10:40:34+03:00 INFO(6): Якась інформація

Підсумковий рядок включає час події, пріоритет та повідомлення. Формат повідомлення, безумовно, може бути змінений, у разі потреби за допомогою методу setFormatter(). За замовчуванням рядок лога описується наступним шаблоном:

%timestamp% %priorityName% (%priority%): %message% %extra%

  1. %timestamp% - це позначка часу
  2. %priorityName% - текстова мітка пріоритету
  3. %priority% - числова мітка пріоритету
  4. %message% - повідомлення
  5. %extra% - необов'язкове значення для додаткової інформації

Якщо Ви захочете змінити формат повідомлення, це робиться таким чином:

$formatter = new Zend\Log\Formatter\Simple("повідомлення %message%" . PHP_EOL);
$writer -> setFormatter($formatter);

Компонент zend-logможе бути також використаний для логування помилок та винятків самого інтерпретатора PHP. Для цього в класі Logger існують два статичні методи: Logger::registerErrorHandler($logger)– для перехоплення помилок та Logger::registerExceptionHandler($logger)- для перехоплення винятків.

Use Zend\Log\Logger;
use Zend\Log\Writer;

$logger = новий Logger;
$writer = new Writer\Stream(__DIR__ . "/test.log");
$logger->addWriter($writer);

// Логувати помилки
Logger::registerErrorHandler($logger);

// Логувати винятки
Logger::registerExceptionHandler($logger);

Як згадувалося раніше, zend-logнадає можливість фільтрації повідомлень для логування, тобто. перед тим як записати повідомлення в лог, ми можемо подивитися задовольняє воно нашим критеріям, і якщо так, то записуємо.

Ось приклад:

$filter = новий Zend\Log\Filter\Priority(Logger::CRIT);

// метод інтерфейсу addFilter Writer
$writer->addFilter($filter);

У цьому прикладі ми будемо логувати тільки ті повідомлення, чий пріоритет менший або дорівнює критичному ( Logger::CRIT).

Повний перелік пріоритетів, визначених у класі Zend\Log\Logger:

Const EMERG = 0; // Аварія: система непридатна до використання
const ALERT = 1; // Тривога: терміново необхідно вживати заходів
const CRIT = 2; // Критична ситуація
const ERR = 3; // Помилка
const WARN = 4; // Попередження
const NOTICE = 5; // Увага
const INFO = 6; // Інформація
const DEBUG = 7; // Дебаг, налагодження

Ми також можемо фільтрувати повідомлення на основі регулярних виразів, тимчасових міток тощо. Таким чином, логування в PHP- це важлива і часом необхідна річ при розробці web-додатків.

Більшість усіх питань на сайті починаються "Допоможіть, не працює, не запускається...". Всі відповіді зводяться до підказок і порад, а ось чому мені раніше не спало на думку написати статтю про те, як і де шукати помилки або про використання ліг я не знаю. Тому заповнюємо прогалину і всім раджу ознайомитись із цією статтею.

Лог (логі) - (Англ. log, можливо Ви зустрічали раніше файли *.log) як правило, текстовий файл у якому в хронологічному порядку йде перелік подій, журнал подій, щоденник, запис, протокол тощо. Логи створюються різними програмами, сервісами, операційними системами. Для кожної програми може створюватись свій лог (текстовий файл).

При розробці сайту, для веб-розробника цінними будуть створені логи:
1. логи на рівні операційної системи:
- Мій комп'ютер - Панель керування - Адміністративні інструменти - Перегляд подій
- Мій комп'ютер - Виконати - "eventvwr.msc"


Сюди потрапляють записи про всі події операційної системи Windows. В тому числі, тут, на вкладках:
- Custom Views / Administrative events
- Windows Logs/System

Ви можете знайти логи пов'язані з сервісом Apache (якщо веб-сервер Apache запущений як сервіс) та інші помилки, викликані, наприклад, розширеннями (extension) php. За великим рахунком, сюди заносяться всі помилки Windows. Apache, як сервіс, вважається частиною Windows, тому якщо при запуску сервісу Apache виникає будь-яка помилка, Вам потрібно шукати розшифровку і цієї помилки тут . Далі, якщо розшифровка помилки в логах, не дає Вам зрозуміти в чому ж проблема, скопіюйте основні частини лога в google і шукайте схожі проблеми. З великою ймовірністю Ви знайдете відповіді, які допоможуть Вам. При запуску будь-якої гри, програми, сервісу, коли виникає помилка, у логах з'являється новий запис з детальнішим описом помилки. Відштовхуючись від цього, Ви завжди можете знайти відповідь в інтернеті.

2. логі на рівні Apache:
Крім Windows логів, сам Apache створює власний лог у вигляді текстового файла. Під час встановлення та налаштування веб-сервера Apache у файлі httpd.confє рядок: ErrorLog "C:/apache/error.log" де, "C:/apache/error.log" шлях до файлу-логу веб-сервера Apache. Встановіть свій шлях або просто запам'ятайте, у разі виникнення помилок під час запуску Apache, Вам потрібно відкрити цей файл і знайти останні записи, де будуть відображатися причини помилок. Крім цього веб-сервер Apache дозволяє створювати логи окремо для кожного віртуального хоста. Приклад віртуальних хостів у файлі conf/extra/httpd-vhosts.conf:


DocumentRoot "C:/apache/symfony/www/web"
ServerName symfony
ServerAlias ​​www.symfony
ErrorLog "C:/apache/symfony/error.log"
CustomLog "C:/apache/symfony/access.log" common


DocumentRoot "C:/apache/phpmyadmin"
ServerName phpmyadmin
ServerAlias ​​www.phpmyadmin
ErrorLog "C:/apache/phpmyadmin/error.log"
CustomLog "C:/apache/phpmyadmin/access.log" common

3. логі на рівні php:
при налаштуванні конфігурації php у файлі php.ini, для налаштування відображення логів, знаходимо наступні рядки:

error_reporting = E_ALL & ˜E_NOTICE & ˜E_STRICT //види і типи логованих помилок, що відображаються
log_errors = On //включаємо логування
log_errors_max_len = 1024 //Визначаємо максимальний розмір файлу логів (1024 байт)
error_log = php_errors.log //Вказуємо ім'я файлу в якому зберігатимуться логи, ці файли будуть створюватися в корені Вашого віртуального хоста. Для кожного хоста буде створено свій файл.

Крім того, що у файл будуть записуватись всі php помилки, Ви також можете створювати логи під час виконання php скрипту за допомогою функції error_log . Це може бути корисно, якщо Ви активно використовуєте в коді try...catch, у такому випадку, скрипти не будуть завершуватися у разі критичних помилок, а в логах завжди будуть помічені всі непередбачені помилки.

try (
$ r = 5/0;
) catch (Exception $exc) (
error_log($exc->getMessage());
}

За результатами виконання цього коду, файл логів php_errors.logбуде вставлено рядок на кшталт такий:

PHP Warning: Division by zero in C:\apache\test\www\index.php on line 5

Ось і все, використовуйте логи скрізь де тільки можна, і Ви швидко знаходитимете помилки в коді і причини неправильної обробки даних.

483

Під логуванням у PHP мається на увазі те, про які типи помилок буде повідомляти вам ваш веб-додаток/сайт/php-скрипт і яким чином.

Існує 2 (3) основних способи отримання помилок від програми:

  1. Виведення цих помилок безпосередньо на екран
  2. Запис цих помилок у спеціальний лог-файл
  3. або відразу обидва варіанти: виведення цих помилок на екран і запис їх у спеціальний лог-файл

Як правило, практикується таке, що при розробці (на локальному оточенні) всі помилки показуються прямо на екрані, щоб їх було легше і оперативніше відловлювати та виправляти, а в бойовому середовищі (на продакшені) помилки не показуються взагалі (т.к. користувача (це марна інформація) і вся інформація про них пишеться в лог-файл, який розробник регулярно переглядає.

Налаштування для логування помилок

  1. error_reporting- Це найголовніший параметр. Він відповідає за те, повідомлення про помилки яких типів будуть відображатися/писатися в лог-файл. Я вважаю, що тут існує тільки 2 опції, які можна використовувати
    • -1 (або E_ALL) - повідомляється про всі типи помилок;
    • 0 — не повідомляється про жодні типи помилок

    Рекомендую використовувати виключно -1 (або E_ALL).
    Т.к. додаток не повинен мати жодних помилок, в принципі. Цю опцію можна задати через конфігураційний файл у php.ini або прямо в php-скрипті за допомогою виклику функції error_reporting:

    Error_reporting(-1); error_reporting(E_ALL);

    До речі, це єдина опція, яка є в мові PHP у вигляді функції. Всі інші опції можна встановити виключно за допомогою правки конфігураційного файлу php.ini або виклику функції ini_set()передавши до неї, відповідно, необхідний параметр та значення для нього.

  2. display_errors— цей параметр відповідає за безпосередній показ помилок на екрані після того, як він, власне, стався. Цей параметр може мати значення 0 або 1 або On/Off. Тобто. або показувати помилки на екрані, або ні.
  3. display_startup_errors- Ця опція відповідає за показ помилок, що відбулися після запуску PHP. Наприклад, якщо в файлі конфігурації є синтаксична помилка, то інформація про неї буде показана. Цей параметр може мати значення 0 або 1 або On/Off.
  4. log_errors— дана директива відповідає за записування повідомлень про помилки в лог-файл. Цей параметр може мати значення 0 або 1 або On/Off. Тобто. записувати помилки в балку чи ні.
  5. error_log— дане налаштування відповідає за шлях до файлу (лог-файлу), в який будуть записуватися всі помилки програми, що відбулися.
  6. html_errors— ця опція відповідає за формат відображення помилок програми. Якщо задана як 1 або On, то помилку буде показано за допомогою HTML'а, тобто. буде trace походження помилки і все буде достатньо інформативно та барвисто. Якщо значення цієї настройки задано як 0 або Off, то помилки будуть відображатися у вигляді звичайного тексту у вигляді невеликого числа рядків.

1. Налаштування для відображення помилок на екрані




ini_set("html_errors", 1);
ini_set("log_errors", 0);

2. Налаштування для запису помилок до лог-файлу

Error_reporting(-1); // ini_set("error_reporting", -1);
ini_set("display_errors", 0);
ini_set("display_startup_errors", 0);
ini_set("log_errors", 1);

3. Налаштування для одночасного відображення помилок та їх запису до лог-файлу

Error_reporting(-1); // ini_set("error_reporting", -1);
ini_set("display_errors", 1);
ini_set("display_startup_errors", 1);
ini_set("log_errors", 1);
ini_set("html_errors", 1);
ini_set("error_log", "/var/log/php/error.log");

Так само, ці опції можна задати і в конфігураційному файлі php.ini або у файлі вашого віртуального хоста.

В інтернеті існує велика кількість сервісів, що надають послуги з обліку Вашого сайту. Ці послуги надаються як платно, так і безкоштовно. Наприклад, можна навести LiveInternet. Цей сервіс є досить широким в інтернеті і майже кожен сайт користується його послугами. Власник має докладну статистику щодо відвідування його сайту.

Безперечно немає! Дані послуги незамінні в обліку відвідуваності сайту та подальшого вивчення поведінки користувачів. Але в цій статті хочу розповісти, як можна зробити на сервері лог-файл візитів відвідувачів.

Лог-файл візитів

Даний лог файл буде дуже корисним, для обліку візитів та переглядів відвідувачами Ваш сайт. Для створення лог-файлу використовується скрипт, написаний мовою PHP. Скрипт досить простий у розумінні та встановленні на сайт.

Скрипт php для створення лог-файлу

Скрипт записує точний час заходу на сайт, визначає браузер відвідувача, і що найголовніше – визначає, звідки прийшов відвідувач до Вас. Запис лог-файлу відбувається при кожному відображенні до тієї чи іншої сторінки сайту. Тобто, власник сайту може подивитися в лог файлі, які конкретно переглядав сторінки сайту відвідувач за певним IP і часом, використовуючи певний браузер.

Листинг скрипту запису даних у лог-файл

Встановлення скрипту

Збережіть скрипт у шаблоні або у зовнішньому файлі users.php. Для того, щоб вставити скрипт у сторінку, використовуйте наступний код.

На серйозних сайтах дивно бачити, коли помилки виводяться користувачеві в браузер у найнесподіваніших місцях. Чому вони з'являються – це окрема розмова. Але чому вони виводяться? Адже текст помилок є інформацією для дебага і призначений для розробника, а не для клієнта.

Крім того, саме ця службова інформація зазвичай допомагає злим хакерам ламати сайт. Як класичний приклад можна навести варіант з виведенням запиту при помилці: "Велике спасибі. Підставляємо після "WHERE id=..." рядок "0 OR 1>0" та запит виконується по всій таблиці. Якщо запит на видалення, то самі розумієте, весело =). Тому я завжди змінні у запитах укладаю у лапки. На всякий випадок...

Але я захопився. Сьогодні не про це. Сьогодні поговоримо про те, як уникнути виведення помилок клієнту, зберігши при цьому всі повідомлення вебмайстру на згадку.

Почнемо, мабуть, із короткого огляду видів помилок у РНР.

Таблиця 1. Опис помилок у PHP4(оригінальний список)
Числове
значення
Константа Опис Ловиться/ні
1 E_ERROR Фатальні помилки. Наприклад, помилка при зверненні до пам'яті. Виконання скрипта у своїй переривається. ні
2 E_WARNING Попередження (не фатальні помилки). Виконання скрипта не переривається. так
4 E_PARSE Помилки під час аналізу синтаксису. Генеруються парсером. ні
8 E_NOTICE Зауваження (менш серйозні помилки, ніж попередження). Вказують на ситуацію, яка може спричинити серйознішу помилку, але можуть траплятися і в процесі нормальної роботи скрипту. так
16 E_CORE_ERROR Помилки під час завантаження РНР. Аналог E_ERROR, що генерується ядром РНР. ні
32 E_CORE_WARNING Попередження під час завантаження РНР Аналог E_WARNING генерується ядром РНР. ні
64 E_COMPILE_ERROR Фатальні помилки під час компіляції коду. Аналог E_ERROR, генерується зендівським двигуном. ні
128 E_COMPILE_WARNING Попередження під час компіляції коду. Аналог E_WARNING, генерується зендівським двигуном. ні
256 E_USER_ERROR Користувацька помилка. так
512 E_USER_WARNING Попередження користувача. так
1024 E_USER_NOTICE Зауваження користувача так

Нас цікавлять помилки, які ми можемо перехопити. До них відносяться: E_WARNING, E_NOTICE та E_USER_*. Інші види помилок перехоплення не піддаються або через те, що відбуваються вони ще до закінчення завантаження самого ядра РНР, або через те, що відбуваються на етапі синтаксичного аналізу та компілювання РНР-коду, тому їх висновок доведеться просто відключити:

ini_set ("display_errors", 0);

Але я припускаю, що наші скрипти досить налагоджені, щоб у них не було елементарних синтаксичних помилок, тому ми нічого не повинні втратити.

За замовчуванням рівень помилок у РНР має значення E_ALL & ~E_NOTICE (або 2039 у числовій формі), що означає, що ми пропускаємо повз вуха зауваження, але повідомляємо про всі інші помилки.

Тому змінимо рівень виведення помилок на E_ALL:

error_reporting (E_ALL);

Тепер перевизначимо хендлер помилок і підставимо замість нього нашу функцію, яка і займатиметься тепер обробкою помилок:

set_error_handler ("user_log");

Розглянемо цю функцію докладніше. Їй передаються 5 параметрів:

  • код помилки
  • текст помилки
  • ім'я файлу, в якому сталася помилка
  • рядок у файлі
  • масив змінних

Повертати цю функцію нічого не зобов'язана. Так як ми збираємося переглядати потім лог помилок, то треба зробити запис лога, наприклад, файл так, щоб нам потім було зручно з ним працювати.

=(LOG_FILE_MAXSIZE*1024)) ( //перевіряємо налаштування, якщо встановлено лог_ротейт, //то "зсуваємо" старі файли на один вниз і створюємо порожній лог //якщо ні - чистимо і пишемо замість старого лога if (LOG_ROTATE===true ) ( $i=1; //вважаємо старі логи в каталозі while (is_file(LOG_FILE_NAME.".".$i)) ( $i++; ) $i--; //у кожного з них по черзі збільшуємо номер на 1 while ($i>0) ( rename(LOG_FILE_NAME."..".$i,LOG_FILE_NAME. "." .(1+$i--)); ) rename (LOG_FILE_NAME,LOG_FILE_NAME.".1"); touch (LOG_FILE_NAME); ) elseif(is_file(LOG_FILE_NAME)) ( //якщо пишемо логи зверху, то видалимо //і створимо заново порожній файл unlink(LOG_FILE_NAME); touch(LOG_FILE_NAME); ) ) ) /* перевіряємо чи є такий файл якщо ні - чи можемо ми його створити якщо є - чи можемо ми писати в нього */ if(!is_file(LOG_FILE_NAME)) ( if (!touch(LOG_FILE_NAME))) !is_writable(LOG_FILE_NAME)) ( return "can\"t write to log file"; ) //зверніть увагу на функцію, якою ми пишемо лог. error_log($err_str, 3, LOG_FILE_NAME); ) ?>

Можна було б, звичайно, використовувати більш логічне для таких цілей сховище - базу, але ж помилки здебільшого виникають саме при роботі з базою, тому я б на неї не покладався.

Власне, це все. Решта, я думаю, не складе вам праці, особливо, якщо користуватися функціями file (); & explode(); . А якщо таки складе, то ви можете скористатися [ось цим кодом].

Передбачаючи питання "чому я не використовував CSV, який, здавалося б, логічно використовувати в цій ситуації?", відповідаю: повідомлення про помилки можуть містити невідому кількість службових символів (ака ком і крапок з комою), що явно ускладнило б розбір CSV. Та й не збираюся я переглядати лог в Екселі.

Ще різні думки на цю тему:

  • при старінні лога gz" іпувати файл і складати його в архів;
  • те саме, але з посилкою на пошту;
  • при виникненні критичних помилок - надіслати мейл (див. приклад з мануалу за функцією