Ultimate Guide to Logging

Your open-source resource for understanding, analyzing, and troubleshooting system logs

PHP Logging Basics

This guide explores the basics of logging in PHP, where to find PHP logs, and how these logs help you more effectively troubleshoot problems and monitor your PHP application. There are a couple of different elements you’ll want to consider logging:

  1. Errors emitted by the PHP engine itself when a core function fails or if code can’t be parsed
  2. Custom errors that your application triggers, usually caused by missing or incorrect user input
  3. Activities in your application that you may want to analyze at a later time, such as recording when a user account is updated or content in a CMS is updated

Configuration Settings

Let’s start by surveying how the PHP engine can be configured to display and log error output. These settings are useful to review if you’re setting up a new server or trying to figure out where errors are being logged on a server that someone else has configured.

Default Configuration

By default, PHP configuration pertaining to errors is found in the following directives within the configuration file php.ini. Here is where you can find your php.ini file in common distributions of Linux:

Table of Default Configuration Paths

OS Path Type
Debian-Based (Debian, Ubuntu, Linux Mint, etc.) /etc/php5/apache2/php.ini Apache module
Redhat/Fedora/CentOS /etc/php.ini All PHP versions
OpenSUSE /etc/php5/apache2/php.ini Apache module

Here are common directives you can configure in this file relevant to logging:

Table of Logging Directives

Directive Default Setting Recommend Production Server Setting Description
*docref_root 1 0 An error format containing a documentation page reference
**display_errors 1 0 Defines whether error output is sent along with regular output
display_startup_errors 0 0 Whether to display PHP startup sequence errors
error_append_string null null String to output after an error message
***error_log null “/path/to/log/file” File path to a writable (by web server user) error log file. This will be the default destination used by the error_log function.
error_prepend_string null null String to output before an error message
error_reporting null E_ALL(or 32767) Sets the error reporting level
log_errors 0 1 Defines whether application errors are logged
log_errors_max_length 1024 0 Sets the maximum byte length of log messages. “0” represents no maximum.
ignore_repeated_errors 0 0 Do not log repeated messages
ignore_repeated_source 0 0 Ignore message source when ignoring repeated messages
report_memleaks 1 0 Reports memory leaks detected by the Zend memory manager
track_errors 0 1 Last error message available in $php_errormsg

* A development environment-only setting.

** Change to “0” for web-facing servers as a security measure.

*** If set to “syslog”, will send errors to the system log.

Run-Time Configuration

PHP provides a number of functions that pertain to run-time configuration directives. The two most common are:

These can be used in an application to override the default settings in php.ini. You may need to look for calls to ini_set in your code if the application isn’t behaving as expected. For example, if you set display_errors to 1 in php.ini, but you still don’t see error output, chances are it’s being overridden somewhere.

Default Error Logs

To log a new error to your web server using PHP, you can use the error_log function. It logs to different destinations depending on the provided message type. The following table shows the default location for your error logs.

Nginx Apache
/var/log/nginx/error.log /var/log/apache2/error.log

Built-In Logging Functions

PHP has a number of built-in logging functions. We’ll discuss logging libraries in another section.

Error-Handling Functions

Here are functions used to facilitate application error logging. The following table lists PHP functions related to logging, with a few examples following.

Table of Error Handling Functions

Function Description Parameter Notes
error_log Sends a message to a defined error handler Required message string, with optional parameters: type, target log file path, and headers as required
error_clear_last Clear the last error message Void
error_get_last Get the last error message Void
error_reporting Sets the error reporting level Constant or associated integer
open_log Opens a connection to the system logger. Allows for swapping system logging facilities. A required string identifier followed by a constant tailoring the logging connection behavior followed by a constant tailoring log handling
set_error_handler Sets a user-defined handler function A callable error handler string and optional error reporting constant or integer
set_exception_handler Sets a user-defined exception handler function A callable exception handler string
syslog Logging to the system log A required logging priority followed by a required message
trigger_error Triggers a user-level message A message string and optional error constant or integer
restore_error_handler Restores the previous error handler. Reverts set_error_handler
restore_exception_handler Restore a previously defined exception handler. Reverts set_exception_handler


This function sends a log entry to the specified system or custom log. The below example will log an error message, specified in the first parameter, as a type 3 and to the target error log. See the error_log documentation for type specification.


error_log(‘Invalid input on user login’, 3, ‘/var/www/example.com/log/error.log’);


You can use the E_USER_* error levels in your application to report errors with varying severity for logging. Like the core constants, there are levels for fatal errors (E_USER_ERROR), warnings (E_USER_WARNING), and notices (E_USER_NOTICE). In your code, use the trigger_error function for custom errors. If your code does encounter an error, use trigger_error with a descriptive message to let your error handler know that something went wrong instead of using the die() function to abruptly terminate everything.

This function takes two parameters, an error message string and an error level, as you can see below.



function foo($email) {
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        trigger_error(‘Supplied value is not a valid email’, E_USER_WARNING);
    // rest of function continues


Another way of logging errors is to send them directly to the system using the syslog function. It takes the error level as the first parameter (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG); the second parameter is the actual error message.

Example (Using openlog for the Connection)

syslog(LOG_WARNING, "User #14 is logged from two different places.");

The above will log a new message to /var/log/syslog:

Aug 3 10:45:15 vaprobash php: User #14 is logged from two different locations.

PHP Log Format

When logging data, you have to make sure you follow a meaningful formatting convention including fields such as date/time, filename, message, etc. This allows you to better analyze and search your data. The following example writes a log message to an error.log file and adds the current date and filename.

$date = date("Y-m-d h:m:s");
$file = __FILE__;
$level = "warning";

$message = "[{$date}] [{$file}] [{$level}] Put your message here".PHP_EOL;
// log to our default location

// error.log
[2015-08-14 09:08:23] [/var/www/html/index.php] [warning] My error message here!

You can also make it a separate function to handle log formatting. The same technique is used by the Monolog package.

function logger($message, array $data, $logFile = "error.log"){    
    foreach($data as $key => $val){   
      $message = str_replace("%{$key}%", $val, $message);    
      $message .= PHP_EOL;    

    return file_put_contents($logFile, $message, FILE_APPEND); } 

logger("%file% %level% %message%", ["level" => "warning", "message" =>"This is a message", "file" =>__FILE__]);
/var/www/html/test.php warning This is a message

You can check our Apache logging guide for more details about configuring your web server logging format.

Logging Levels

PHP provides a variety of levels for identifying the severity of errors encountered when your application runs. The error levels indicate if the engine couldn’t parse and compile your PHP statements, couldn’t access or use a resource needed by your application, all the way down to letting you know if you have a possible typo in a variable name.

Although the error levels are integer values, there are predefined constants for each one. Using the constants will make your code easier to read and understand and keep it forward-compatible when new error levels are introduced. Common error levels encountered include:

  • E_ERROR: These are fatal run-time errors that halt script execution, such as running out of memory or calling a function that does not exist.
  • E_WARNING: A run-time warning is an error that does not halt script execution but can cause your code to not work as expected. For example, fopen() will return false and trigger an E_WARNING if you try to open a file that does not exist. The rest of your code won’t be able to read or write to the requested file.
  • E_PARSE: These are compile-time parse errors, like missing parentheses or semicolons.
  • E_NOTICE: Notices indicate possible bugs, such as using a variable that doesn’t exist in an expression. This could mean the variable name has a typo or needs to be assigned some value earlier.
  • E_STRICT: These errors indicate places in your code that may stop working in future versions of PHP. Using mysql_query() will trigger a strict error because the extension that provides that function is deprecated, and in fact is not bundled in PHP 7.

Error-level constants are used by the error_reporting function to indicate which types of errors should be displayed or logged. You can use bitwise operators to customize your applications’ verbosity:


// report all errors except notices and strict errors 
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT);

A Note on Error Suppression

You’ve probably encountered code that prefixes a function call with the @ symbol, which is the error control operator. This suppresses any errors emitted by that function. In the end, this means that if the function fails, you won’t see any errors related to it on screen or in your logs. It’s a very lazy and bad habit, and you should avoid picking it up. Remember, you can always set a custom error handler to catch errors.


// don’t do this
$fh = @fopen(‘example.csv’, ‘r’);

If you’re trying to fix a bug but don’t see any errors, search your code for function calls prefixed with one @. You can also install and enable the scream extension which disables this behavior and ensures that all errors are reported.

Best Practice: Set the error level to “E_ALL” unless a specific use case is required.

Application Error Logging

By “application error logging,” we mean errors generated by PHP application code. In Linux distros, application logging is disabled.

You can change the default configuration to enable application error logging either in the php.ini file with the log_errors directive or in one of two ways: by editing the php.ini file parsed by the web server, or via run-time settings using the ini_set() PHP function within application code.

Via php.ini (applicable directives)

log_errors = 1

Changing this directive requires a web server reload or restart.

Via ini_set()

ini_set(‘log_errors’, 1);

This run-time setting is necessary only if the log_errors directive is not enabled as a general configuration for a particular reason, and your application code requires it.

Best Practice: Be sure to enable error reporting and logging on web-facing servers and disable displaying errors in response. Displaying errors in response on a live environment is a serious security mistake and must be avoided. The display_errors directive is mentioned in the following configuration due to this security concern.

Custom Logging with JSON

Logging user actions and activities provides a valuable source of information for seeing which features get used, the steps users take to accomplish tasks, and for tracking down and recreating errors that they encounter. While you can log this activity to a local file, typically you’ll want to log this to an existing logging service to make it easier to analyze and report. Instead of inventing your own format—that would require a custom parser—use JSON as your logging format. For more on getting the most out of JSON logging, see 8 Handy Tips to Consider When Logging in JSON.

Best Practice: Use JSON to log user behavior and application errors. Do this from the beginning so you don’t have to convert or lose older logs.

PHP arrays and object can be converted to JSON string using json_encode(). Likewise, JSON can be reconstituted as a native PHP construct by using json_decode(). A few code examples follow that demonstrate the functions first, then using them to log and parse an entry.

Example: Using the json_encode() function with an associative array

$logdata = ['msg' => 'Invalid username', 
            'code' => '601', 
 'url' => $_SERVER['REQUEST_URI'],
           'username' => 'ae2ivz!'];
$jsonlogdata = json_encode($logdata);
//Produces: {"msg":"Invalid username","code":"601","url":"http://example.com/foo","username":"ae2ivz!"}

Example: Using the json_decode() function

$jsonlogdata = '{"msg":"Invalid username","code":"601","url":"http://example.com/foo","username":"ae2ivz!"}';
$logdata = json_decode($jsonlogdata); //produces stdclass object
$logdata = json_decode($jsonlogdata, true); //produces associative array


Exceptions are an elegant way to handle application errors and subsequent logging. They are either thrown and caught within a try/catch/finally block, or handled by a custom exception function.

Exceptions are used when parts of your application logic can’t continue the normal flow of execution and reach an exceptional state.

The Base Exception

PHP has a base Exception class that is available by default in the language. The base Exception class is extensible if required.

Example: (assumes directive log_errors = 1)

        throw new Exception("Something failed", 900);
} catch (Exception $e) {    
    $datetime = new DateTime();    
    $datetime->setTimezone(new DateTimeZone('UTC'));    
    $logEntry = $datetime->format('Y/m/d H:i:s') . '/' .              $e->getMessage(). '/' .        
        $e->getCode() . '/' .        
        $e->getFile() . '/' .        
     // log to default error_log destination    
    //Email or notice someone 

The above example would produce the following error log entry:

2015/07/30 17:20:02/Something failed/900//var/www/html/index.php/4

A Custom Exception

Let’s say you have a certain frequency of a specific type of error and you would like to tailor a custom exception to handle it. In the following example, we’ll create a custom exception called TooManyLoginAttemptsException to handle incidences of too many login attempts.

Define the custom exception class:

class TooManyLoginAttemptsException extends Exception{
    public function __construct($msg, $code){
        parent::__construct($msg, $code);

Define a class to throw the custom exception:

class User{
    public function login(array $credentials){
        if ($this->attempts($credentials) >= 10) {
            throw new TooManyLoginAttemptsException("Too many logging attempts from user {$credentials['email']} at {time()}");
        // attempt login

Code attempt to log in a user catching the custom exception:

try {
} catch (TooManyLoginAttemptsException $ex) {
    // You can add details to your logs here.
    error_log("Too many login attempts from user 
$credentials['email']} at {time()}");

SPL Exceptions

The PHP has a built-in code library called “Standard PHP Library” or SPL. The library has a number of exception classes defined and available for common code-level errors. These exceptions extend from the base PHP Exception class internally. They are different by name only, and can be thrown, caught, and logged.

Define a class to throw an SPL exception: (BadMethodCallException)

The example uses the PHP magic method __call() to catch an undefined method call.

class Adapter{
    public function __call($method, $options){
        $adapter = $this->getAdapter();
        if (!method_exists($adapter, $method)) {
            throw new BadMethodCallException("Unknown method '{$method}'");

Code attempt to use an undefined method:

try {
} catch (BadMethodCallException $e) {
    //Log the error

PHP 7 Exceptions Changes

In PHP 7, The exception hierarchy has changed in the core. A “Throwable” interface is introduced, and the Exception and Error classes implement it. This provides the ability to catch PHP internal exceptions, like TypeError, ParseError, etc. Up through PHP 5, fatal errors cannot be handled gracefully since they immediately terminate program execution. Full documentation is listed here.

The “Throwable” interface cannot be implemented by custom exceptions directly and must extend from the base Exception class for implementation.

Code attempt catches “Throwable” type hint:

try {
// Your code here
} catch(Throwable $ex) {
// You can catch any user or internal PHP exceptions


Let’s say that a login error occured, and we want to capture every failed login attempt.

error_log("Failed to login using $username and $password at $timestamp");

I’m using Debian Ubuntu for my examples. This will add a new entry in my `/var/log/apache2/error.log`. Remember, if omitted, the default destination is determined by your error_log ini setting.

// error.log
Aug 5 9:45:01 vaprobash php: Failed to login using john_doe and 12345 at 1440543165

You can read more about logging to Apache in this guide. You can specify the message type parameter to log to a different location; see the PHP documentation for the list of available parameters. The 3 value on the second parameter is used to log to a specified file that we pass as the third parameter.

error_log("Failed to login using $username and $password at $timestamp" . PHP_EOL, 3, "/var/log/apache/myerror.log");