SAPI streams и ошибки

Мы знаем, что PHP имеет собственные обертки доступа к стандартным I/O потокам (streams). Так же мы знаем, что в PHP-мире есть множество SAPI, но сегодня мы поговорим о 2-х часто используемых: CLI и FPM

FPM SAPI

Тут мы имеем 3 потока:

  • php://input — для чтения raw body из POST запроса
  • php://output — аналог функции print и echo.
  • php://stderr — для записи в поток ошибок

нюанс: за 1 интерпритацию из php://input  можно прочитать только 1 раз, при чтении второй раз - результатом будет пустая строка.

CLI SAPI

Тут мы имеем стандартные 3 потока:

  • php://stdin — для чтения из потока ввода (пример использования)
  • php://stdout — для записи в поток вывода
  • php://stderr — для записи в поток ошибок

Обратите внимание, что в CLI SAPI есть нюансы, а так же две глобальные переменные:

  • $argc
  • $argv

Обработчик ошибок

Мы знаем, что есть множество настроек и возникающие ошибки можно обрабатывать написав собственный обработчик ошибок, например:

function handleError($type, $message, $file = '', $line = 0, array $extra = array()) {
    $text = 'type='.$type.PHP_EOL.
    'message='.$message.PHP_EOL.
    'file='.$file.PHP_EOL.
    'line='.$line.PHP_EOL;
    error_log('handleError: '.$text);
}
set_error_handler('handleError', E_ALL);

Ошибки

Многие наивно полагают, что обработчик будет задествован при записи в поток php://stderr:

file_put_contents('php://stderr', 'my error', FILE_APPEND);

на самом же деле, обработчик ошибок никак не отреагирует на то, что Вы пишите в php://stderr т.к. это всего лишь поток.

Однако, послать ошибку в текущий обработчик можно с помощью функций:

  • trigger_error('my error', E_USER_ERROR);
  • error_log('my error');// если error_log задан (см. ниже), то обработчик не получит уведомление об ошибке

По-умолчанию параметр error_log не задан и когда в PHP возникает ошибка, то PHP отдает ее пользовательскому обработчику ошибок, но если пользовательский обработчик ошибок не задан, то PHP отправляет ошибку в php://stderr, а Nginx читает из стандартного потока stderr, и записывает в /var/log/nginx/site.error.log

*напомню, что мы можем тестировать PHP не используя nginx »

FPM SAPI и Nginx

На продакшен среде чтобы логировать ошибки и в тоже время пользователь не видел ошибки, выставляют:

error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
display_errors = Off

Если error_log не задан:

При возникновении ошибки PHP-FPM может действовать по-разному в зависимости от следующего параметра:

display_errors = 0 — PHP-FPM отдает Nginx-у хеадер 500 (по-умолчанию в php.ini), ошибки пишутся в php://stderr, а Nginx читает из стандартного потока stderr, и записывает в /var/log/nginx/site.error.log

display_errors = 1 — PHP-FPM отдает Nginx-у хеадер 200 а ошибки пишет в поток php://output

Обратите внимание, что PHP будет выполнять эти действия только, если параметр log_errors = On (по умолчанию On)

Если error_log задан:

Для разработчика важно видеть все ошибки и лично мне приятно смотреть их в одном файле, поэтому я в конфиге /etc/php5/fpm/pool.d/www.conf указал:

php_admin_value[error_log] = /var/tmp/error.log

Пробую:

<?php
error_reporting(E_ALL);
ini_set('display_errors', 0);

$text = 'error'.time().PHP_EOL;

file_put_contents('php://stderr', $text, FILE_APPEND);// способ 1 (не записывает ошибку в лог-файл И не отправляет ее в обработчик И nginx ее не прочтет т.к. это не настоящий php://stderr)

$stream = fopen('php://stderr', 'w+');// способ 2 (не записывает ошибку И не отправляет ее в обработчик И nginx ее не прочтет т.к. это не настоящий php://stderr)
fwrite($stream, $text);

error_log('1'.$text, 4);// способ 3 (не записывает ошибку в лог-файл), но отправляет ошибку в настоящий php://stderr поэтому nginx запишет ошибку в /var/log/nginx/site.error.log
error_log('2'.$text);// отправит ошибку в системный регистратор PHP, который запишет ошибку в лог-файл
trigger_error('3'.$text, E_USER_ERROR);// записывает ошибку или отправляет ошибку в обработчик ошибок (если таковой задан)

и все бы хорошо, но все что пишется в php://stderr не пишется в настоящий php://stderr при этом в мануале написано, что можно все писать в error_log-файл благодаря:

catch_workers_output = yes

Но, к сожалению, включение параметра catch_workers_output не помогает (service php5-fpm restart делал). Протестировал на версиях:

  • PHP 5.6.11-1ubuntu3.1
  • PHP 5.6.14-0+deb8u1 (fpm-fcgi) (built: Oct 4 2015 16:13:04)

Возможно в следующих версиях PHP-FPM это будет работать.

Итог: придется не использовать параметр error_log и использовать свой хэндлер ошибок.

Источники: 1 - 2 - 3 - 4 - 5

Оцени публикацию:
  • 1,5
Оценили человек: 1

Похожие статьи:

Справочники и учебники:


Предложения и пожелания:
Ваше имя:
Ваш E-mail:
Введите изображенные цифры:
Captcha
Главная
X

youtube.com/watch?v=7hFivbgIEqk

При полном или частичном использовании материалов данного сайта, ссылка на сайт "yapro.ru" обязательна как на источник информации.
Автоматический импорт материалов и информации с сайта запрещен.
Copyrights © 2007 - 2017 YaPro.Ru

Главная » Веб-мастеру » PHP »