Регистрация и вывод ошибок в PHP
Регистрация ошибок в PHP не самая простая и интуитивно понятная. Это включает в себя настройку нескольких параметров конфигурации, а также некоторые эксперименты, чтобы привыкнуть. Как только вы все настроите и разберетесь (как вы это сделаете после прочтения этого поста), все станет намного проще, и вы поймете, насколько полезным может оказаться ведение журнала ошибок для вашего приложения — от отладки и устранения неполадок до мониторинга и обслуживания.
Важность регистрации ошибок
Ошибки в программных системах имеют ужасную репутацию, связанную со сбоями и нарушением функциональности. В результате многие из нас часто не осознают важность этих кричащих красных строк, которые привлекают наше внимание к ошибкам, несоответствиям и неточностям в нашем коде — ошибкам, которые могут дорого нам обойтись, если их не заметить. Поэтому некоторым из нас стоит изменить свое отношение к сообщениям об ошибках — отслеживать, регистрировать и систематизировать их — и осознавать их важность.
Есть причина, по которой разработчики и организации создают и используют специальные системы ведения журналов, которые отслеживают ошибки, возникающие на протяжении всего жизненного цикла приложения. Эти журналы предоставляют полезную информацию о том, что пошло не так, когда, где и как это можно исправить.
Теперь, когда мы, надеюсь, убедились, что логирование ошибок — стоящая экспедиция, давайте рассмотрим различные типы ошибок в PHP.
Типы ошибок PHP
В целом, в PHP существует пять типов ошибок:
1. Фатальные ошибки во время выполнения (E_ERROR)
Эти ошибки обычно возникают, когда операция в вашем коде не может быть выполнена. Это приводит к выходу вашего кода. Примером фатальной ошибки может быть вызов функции, которая не была определена в вашем коде, как показано ниже:
<?php function foo() { echo "Function foo called."; } boo(); // undefined function 'boo' ?>
Вывод ошибки:
Fatal error: Uncaught Error: Call to undefined function boo() in code/my-php/index.php:5 Stack trace: #0 {main} thrown in code/my-php/index.php on line 5.
2. Предупреждающие ошибки (E_WARNING)
Предупреждающая ошибка является более мягкой и менее навязчивой, поскольку она не останавливает выполнение. Он представляет собой дружеское напоминание о том, что в вашем коде что-то не так — ошибка, которая может не привести к немедленному сбою или сбою вообще, но предлагает более точный способ сделать что-то, что сделает ваш код более надежным. Эти предупреждения также могут уберечь разработчиков от проблем, которые могут представлять гораздо большую угрозу в будущем. Примером предупреждения об ошибке может быть попытка включения файла в PHP с использованием неправильного пути к файлу, как показано ниже:
<?php include('filename.txt'); // arbitrary file that is not present echo "Hello world"; ?>
Вывод ошибки:
Warning: include(filename.txt): failed to open stream: No such file or directory in code/my-php/index.php on line 2
3. Ошибки синтаксического анализа (E_PARSE)
Ошибки синтаксического анализа также известны как синтаксические ошибки, поскольку они возникают из-за синтаксических ошибок в вашем коде. Эти ошибки возникают во время компиляции вашего кода, заставляя его выйти до запуска. Распространенный пример ошибки синтаксического анализа — отсутствие точки с запятой в конце оператора кода, как показано ниже:
<?php echo Hello world // no quotes or semicolon used ?>
Вывод ошибки:
Parse error: syntax error, unexpected 'world' (T_STRING), expecting ',' or ';' in code/my-php/index.php on line 2.
4. Уведомления об ошибках (E_NOTICE)
Ошибки-уведомления — это незначительные ошибки, возникающие во время выполнения и, как и ошибки-предупреждения, не останавливают выполнение. Обычно они возникают, когда скрипт пытается получить доступ к неопределенной переменной, например, как показано ниже:
<?php $a = 1; $c = $a + $b; // undefined variable $b ?>
Вывод ошибки:
Notice: Undefined variable: b in code/my-php/index.php on line 3
5. Пользовательские ошибки (E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE)
Ошибки пользователя определяются пользователем, т. е. представляют собой пользовательское сообщение, созданное явно из кода для захвата определенного состояния. Эти ошибки вызываются разработчиками вручную с помощью функции trigger_error вместо механизма PHP. Далее они классифицируются как фатальные ошибки, ошибки предупреждений и уведомления, но для простоты мы сгруппируем их все как ошибки пользователя.
Где могут быть выведены ошибки PHP?
Есть два основных места, где мы можем представить наши ошибки в PHP — через встроенные ошибки и специальные файлы журнала ошибок.
Встроенные ошибки
Встроенные ошибки — это те, которые отображаются на вашей веб-странице в браузере или на вашем терминале через STDOUT в среде командной строки. Эти ошибки оказываются весьма полезными во время разработки — разработчики могут отлаживать свой код, исправлять проблемы и получать информацию об общем выполнении. Ниже приведен пример того, как эти ошибки обычно выглядят в браузере:
Хотя это оказывается очень полезным для разработчиков, вы должны быть очень осторожны, гарантируя, что эти ошибки не будут выводиться, когда ваше приложение будет запущено в работу — по двум причинам — удобство для конечного пользователя и безопасность. Вы можете переключить отображение этих ошибок, используя директиву display_error в конфигурации вашей системы. Мы углубимся в это в следующем разделе.
Файлы журнала ошибок
Встроенные ошибки не сохраняются в памяти, т. е. они нигде не сохраняются и просматриваются только до тех пор, пока активен браузер или сеанс терминала. Кроме того, вы захотите иметь их только в среде разработки. И наоборот, как предполагает тема этого поста, регистрация ошибок — более разумный и систематический подход к поддержке крупномасштабных приложений. Они сохраняются в памяти и предоставляют информацию о работе вашего приложения в нескольких компонентах в одном месте, что упрощает мониторинг и устранение неполадок.
Таким образом, PHP позволяет вам направлять все ваши ошибки в определенные файлы журналов; в этих файлах хранятся метки времени, трассировки стека ошибок, настраиваемые сообщения и другая полезная информация об источнике ошибки и способах ее устранения. Вы можете указать путь к вашему пользовательскому файлу журнала, используя директиву error_log конфигурации вашей системы. Вот пример файла журнала ошибок:
Вы также можете записывать свои ошибки в файл системного журнала, который обычно находится в — /var/log/syslog . Мы расскажем об этом в следующем разделе поста.
Теперь давайте посмотрим, как мы можем настроить, где и как мы хотим регистрировать наши ошибки.
Включение и настройка отчетов об ошибках в PHP
Как мы обсуждали ранее, регистрация ошибок в PHP немного менее проста, чем в других языках и фреймворках. Возможно, вам придется настроить несколько параметров в файлах конфигурации, чтобы настроить шаблоны ведения журнала. Например, когда вы устанавливаете PHP на свой компьютер, первоначальная конфигурация поставляется с отключенными некоторыми аспектами регистрации ошибок. Это отличается от системы к системе, и поэтому вам следует вручную проверить эти настройки перед началом работы.
Регистрация параметров конфигурации в файле php.ini
Параметры конфигурации находятся в файле php.ini . Этот файл считывается при запуске PHP и позволяет разработчикам экспериментировать с функциональностью PHP. Обычно этот файл можно найти где-нибудь в каталоге /etc/php в большинстве систем Linux.
Существует множество директив (опций), относящихся к регистрации ошибок в PHP, которые мы можем настроить в этом файле php.ini :
- display_errors (по умолчанию: 1)
Ошибки отображения — это встроенные ошибки, которые мы рассматривали ранее. Эту директиву можно эффективно использовать во время разработки для вывода сообщений об ошибках PHP в браузер или терминал. Однако для приложений в рабочей среде вам, скорее всего, следует отключить эту функцию, чтобы уберечь пользователей от плохой работы с веб-сайтом из-за неясных сообщений об ошибках. Не только это, но это также защищает вас от раскрытия ценной информации о внутренностях вашего приложения в качестве меры безопасности.
- display_startup_errors (по умолчанию: 0)
Как следует из названия, это для вывода любых ошибок, возникающих при запуске PHP. Обычно они не предоставляют никакой ценной информации конкретно о вашем приложении, и поэтому их не нужно включать.
- log_errors (по умолчанию: 0)
Эта директива позволяет вам переключить регистрацию ошибок на указанный путь (в следующей директиве). Поскольку по умолчанию это отключено, было бы целесообразно переключить его на 1 для записи сообщений об ошибках вашего приложения в файл журнала.
- error_log (по умолчанию: 0)
Эта директива позволяет указать путь к файлу журнала. Вы также можете установить для этого параметра значение « syslog », чтобы направлять все ваши сообщения журнала ошибок в системный журнал.
- error_reporting (по умолчанию: ноль)
Директива error_reporting позволяет настроить, о каких уровнях ошибок вы хотите сообщать, а о каких вы можете не сообщать. Например, вы можете использовать директиву, как показано ниже, чтобы сообщать обо всех ошибках:error_reporting = E_ALL
- track_errors (по умолчанию: 0)
Эта директива позволяет вам получить доступ к последнему сообщению об ошибке в глобальной переменной $php_errormsg в вашем коде и может отслеживать ошибки во всем вашем проекте.
После внесения изменений в файл php.ini вам необходимо перезапустить сервер, чтобы изменения вступили в силу.
Функция ini_set()
Однако, если вы не можете найти файл php.ini или предпочитаете переопределить глобальные параметры конфигурации вашего проекта, есть также возможность обновить эти директивы с помощью функции ini_set() в вашем PHP-коде. Например, приведенный ниже код можно использовать для настройки отчетов об ошибках в вашем проекте:
<?php // enabling error logging ini_set('log_errors', 1); // Customize reporting of errors ini_set('error_reporting', E_WARNING | E_ERROR | E_PARSE | E_NOTICE); // specify error log file path ini_set('error_log', '/tmp/my-logs.log'); ?>
Функция error_reporting()
Можно также изменить параметр конфигурации error_reporting , используя функцию error_reporting() внутри вашего кода во время выполнения. Как и в функции ini_set, вы можете использовать побитовые операторы, такие как ИЛИ (|), И (&), НЕ (~) и т. д., при указании уровней ошибок, о которых следует сообщать. Ниже приведены несколько примеров использования этой функции.
// Report only selected kinds of errors error_reporting(E_ERROR | E_PARSE | E_NOTICE);
или
// Report all errors except E_WARNING error_reporting(E_ALL & ~E_WARNING);
Теперь, когда у нас есть системные конфигурации и общая настройка, давайте рассмотрим пример того, как ошибки в коде вашего проекта могут быть зарегистрированы в файлах вашей системы.
Регистрация ошибок в PHP
Во-первых, мы переопределим параметры конфигурации ведения журнала с помощью функции ini_set() , чтобы включить ведение журнала ошибок и указать путь к файлу журнала. Затем мы напишем некоторый ошибочный код, чтобы PHP вызывал ошибку, которую мы хотели бы зарегистрировать.
<?php ini_set('log_errors', 1); // enabling error logging ini_set('error_log', '/path/my-error-file.log'); // specifying log file path echo $b; // undefined variable should raise error ?>
Открыв веб-страницу в нашем браузере, давайте откроем файл «my-error-file.log», чтобы увидеть, было ли зарегистрировано сообщение об ошибке. Вот вывод файла журнала:
[28-Feb-2021 13:34:36 UTC] PHP Notice: Undefined variable: b in code/my-php/index.php on line 5
Как видите, наша ошибка уведомления была зарегистрирована с отметкой времени. Поскольку наш код обнаруживает все больше и больше ошибок, этот файл будет продолжать заполняться соответствующими временными метками. Обратите внимание, что мы явно не отключили display_errors , поэтому эти сообщения об ошибках, скорее всего, будут регистрироваться на веб-странице браузера, чего вы, возможно, захотите избежать во время производства.
Это был пример регистрации ошибок, вызванных PHP, в файлах журналов. Теперь давайте посмотрим, как мы можем создавать и регистрировать пользовательские сообщения об ошибках для нашего приложения.
Функции регистрации ошибок PHP
До сих пор мы рассматривали ошибки, возникающие в PHP — ошибки, связанные с выполнением вашего кода. Тем не менее, часто вы хотели бы также фиксировать пользовательские ошибки с пользовательскими сообщениями об ошибках, характерными для функционирования вашего приложения. Эти так называемые ошибки не обязательно могут привести к сбою вашего кода или остановке его выполнения, но могут указывать на условия, характеризуемые как ошибочные и заслуживающие внимания для вашего приложения. Они могут служить для организации указанием на аномальное поведение, которое команда может захотеть изучить и исправить.
Чтобы облегчить это, PHP предоставляет набор функций, которые мы можем использовать для активного логирования ошибок в нашем коде.
error_log()
Наиболее распространенным методом активной регистрации ошибок является функция error_log() . Это отправляет строковый аргумент для сообщения об ошибке в файл журнала.
error_log (string $message, int $message_type=0, string $destination=?, string $extra_headers=?) : bool
Также требуется много других параметров для отправки сообщений об ошибках по электронной почте или в определенные файлы журналов. Однако, для простоты, мы не будем рассматривать это здесь.
Интересная особенность этой функции заключается в том, что она записывает ваше сообщение об ошибке в файл, указанный в конфигурации (или в системный журнал), независимо от значения директивы log_errors . Давайте возьмем очень простой пример регистрации ошибки при выполнении определенного условия в нашем коде.
<?php ini_set('error_log', '/path/my-error-file.log'); $a = 5; $b = 10; $c = $a + $b; if ($c < 20) { error_log("Sum is less than 20."); // logging custom error message } ?>
Вот вывод файла журнала:
[28-Feb-2021 13:31:50 UTC] Sum is less than 20
Точно так же вы также можете регистрировать значения переменных в своем коде, чтобы предоставить дополнительный контекст о ваших ошибках. Давайте посмотрим на пример для этого:
<?php ini_set('error_log', '/path/my-error-file.log'); $languagesArray = array("PHP", "Python", "Node.js"); error_log("Lorem ipsum. Array data -> ".print_r($languagesArray, true)); ?>
Вот вывод файла журнала ->
[28-Feb-2021 13:49:28 UTC] Lorem ipsum. Array data -> Array ( [0] => PHP [1] => Python [2] => Node.js )
trigger_error()
Функцию trigger_error() можно использовать для создания пользовательской ошибки/предупреждения/уведомления. Вы также можете указать тип ошибки на основе условия. Это позволяет настроить его отчетность и другое поведение, например, используя тип ошибки E_USER_ERROR. Мы можем вызвать немедленный выход кода по сравнению с ошибкой E_USER_WARNING.
trigger_error (string $error_msg, int $error_type=E_USER_NOTICE) : bool
Разница между trigger_error и error_log заключается в том, что первый генерирует только пользовательскую ошибку и зависит от конфигурации ведения журнала вашей системы для обработки этого сообщения об ошибке (независимо от того, отображается оно или регистрируется). error_log , с другой стороны, будет регистрировать ваше сообщение независимо от конфигурации системы.
Вот код того же примера, который мы видели ранее:
<?php ini_set('log_errors', 1); // enabling error logging ini_set('error_log', '/path/my-error-file.log'); // specifying log file path $a = 5; $b = 10; $c = $a + $b; if ($c < 20) { trigger_error("Sum is less than 20.", E_USER_ERROR); echo "This will not be printed!"; } ?>
Это добавляет запись в журнал, аналогичную той, что мы видели ранее, но с уровнем ошибки, а также обычной информацией об источнике ошибки (вывод файла журнала ниже):
[01-Mar-2021 01:16:56 UTC] PHP Fatal error: Sum is less than 20. in code/my-php/index.php on line 10
syslog()
Вы также можете выбрать прямую отправку сообщения об ошибке в системный журнал с помощью функции syslog() .
syslog (int $priority, string $message) : bool
Первый аргумент — это уровень приоритета ошибки — LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_ALERT, LOG_EMERG и т. д. Второй аргумент - это фактический текст сообщения. Вот как эту функцию можно использовать:
<?php // opening logger connection openlog('myApp', LOG_CONS | LOG_NDELAY | LOG_PID, LOG_USER | LOG_PERROR ); // more information about params in documentation syslog(LOG_WARNING, "My error message!"); closelog(); ?>
Это должно отражаться в журнале вашей системы (обычно в /var/log/syslog ) как:
Mar 1 13:27:15 zsh php: My error message!
set_error_handler()
Чтобы настроить обработку всех пользовательских ошибок в вашем коде, PHP позволяет указать пользовательскую функцию обработчика ошибок, чтобы переопределить обработку ошибок по умолчанию. Это позволяет организациям легко изменить способ регистрации ошибок, соответствующий метод отправки сообщений об ошибках и многое другое. В этом помогает функция set_error_handler() .
set_error_handler (callable $error_handler, int $error_types=E_ALL | E_STRICT) : mixed
Он принимает в качестве аргумента нашу пользовательскую функцию-обработчик ошибок, которая определяет обработку наших ошибок и выглядит примерно так:
handler (int $errno, string $errstr, string $errfile=?, int $errline=?, array $errcontext=?) : bool
Он принимает множество параметров, таких как номер ошибки, строка ошибки, соответствующий файл и т. д. Давайте лучше разберемся с этим, используя тот же предыдущий пример:
<?php // custom error handler function -> function myErrorHandler($errno, $errstr, $errfile, $errline, $errcontext){ $message = date("Y-m-d H:i:s - "); // timestamp in error message $message .= "My Error: [" . $errno ."], " . "$errstr in $errfile on line $errline, \n"; // custom error message $message .= "Variables:" . print_r($errcontext, true) . "\r\n"; error_log($message, 3, "/path/my-error-file.log"); die("There was a problem, please try again."); // exit code } set_error_handler("myErrorHandler"); $a = 5; $b = 10; $c = $a + $b; if ($c < 20) { trigger_error("Sum is less than 20.", E_USER_WARNING); } echo "This will not be printed!"; ?>
Здесь мы определяем пользовательскую функцию обработчика ошибок, в которой мы немного изменяем сообщение об ошибке, регистрируем его и выходим из кода. Затем, когда мы используем функцию trigger_error() , ее регистрация обрабатывается указанной выше функцией, которая позаботится обо всем остальном. Вот как выглядит вывод в файле журнала:
2021-03-01 06:58:07 - My Error: [512], Sum is less than 20. in code/my-php/index.php on line 22, Variables:Array ( [a] => 5 [b] => 10 [c] => 15 )
Как видите, это можно использовать для полной настройки регистрации ошибок в приложениях, что позволяет организациям расставлять приоритеты в тех аспектах ошибок и контекстах, которые более важны для их приложения.
Популярные библиотеки журналирования PHP
Благодаря огромной поддержке PHP-сообщества в Интернете существует очень много библиотек ведения журналов, которые стремятся предоставить больше функциональности и упростить общий процесс для разработчиков и организаций. Каждый из известных фреймворков PHP , о которых вы, должно быть, слышали, оснащен встроенными библиотеками ведения журналов. Также теперь установлены стандарты ведения журналов, такие как интерфейс регистратора PSR-3 (рекомендация по стандартам PHP), который определяет стандартизированный интерфейс для библиотек журналирования.
Ниже приведен список некоторых наиболее популярных библиотек ведения журналов в PHP:
Не стесняйтесь проверить их, чтобы увидеть, чего не хватает в журнале ошибок по умолчанию в PHP.
Подведение итогов
В этом посте мы рассмотрели все об ошибках и регистрации в PHP. Мы обсудили важность механизмов ведения журнала в вашем приложении, рассмотрели различные типы ошибок в PHP и изучили различные параметры конфигурации и функции PHP, которые мы можем использовать для эффективного ведения журнала ошибок.