Настройка логов в php. Логирование в PHP с помощью Zend Log Запись в лог файл php

На серъезных сайтах странно видеть, когда ошибки выводятся пользователю в браузер в самых неожиданных местах. Почему они появляются - это отдельный разговор. Но почему они выводятся? Ведь текст ошибок является информацией для дебага и предназначена для разработчика, а не для клиента.

Кроме того, именно эта служебная информация обычно помогает злым хакерам ломать сайт. В качестве классического примера можно привести вариант с выводом запроса при ошибке: "you have an error in query near WHERE id= " ... Большое спасибо. Подставляем после "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)) { return "can\"t create log file"; } } elseif(!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"иповать файл и складывать его в архив;
  • то же, но с посылкой на почту;
  • при возникновении критических ошибок - слать мэйл (см. пример из мануала по функции

В интернете существует большое количество сервисов, предоставляющие услуги по учету посещаемости Вашего сайта. Данные услуги предоставляются как платно, так и бесплатно. К примеру, можно привести LiveInternet. Этот сервис довольно широкий в интернете и почти каждый сайт пользуется его услугами. Владелец имеет подробную статистику о посещении его сайта.

Спору нет! Данные сервисы незаменимы в учете посещаемости сайта и дальнейшего изучения поведения пользователей. Но в данной статье я хочу рассказать, как можно сделать на сервере лог-файл визитов посетителей.

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

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

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

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

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

Установка скрипта

Сохраните скрипт в шаблоне, либо во внешнем файле users.php. Для того, чтобы вставить скрипт в страничку используйте следующий код.

В каждом реальном PHP приложений время от времени возникают ошибки и исключения, выскакивают предупреждения, сообщения. Если мы не будим записывать эту информацию (логировать), то в один прекрасный момент станет невозможным понять, в какой же части приложения возникают эти ошибки и исключения, и, соответственно, не сможем решить их. К тому же, существуют такие ситуации, когда логирование событий, действий просто необходимо, как в случае, с входом пользователя в систему и выходом из нее, например.

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

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

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

Сomposer require zendframework/zend-log

Используется следующим образом (для примера используется файл index.php в корне проекта):

Require "vendor/autoload.php";

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

$logger = new Logger;

// отправляем ошибки в консоль
$writer = new 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 = new Logger;
$writer = new Writer\Stream(__DIR__ . "/test.log");
$logger->addWriter($writer);

// Логировать ошибки
Logger::registerErrorHandler($logger);

// Логировать исключения
Logger::registerExceptionHandler($logger);

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

Вот пример:

$filter = new 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