By Matej Elias
Last Validated on May 10 2021 · Originally Published on May 10, 2021 · Viewed 1.2k times

Introduction

In this tutorial, we will look at PHP logging. Specifically we will learn how to configure logs, how to show logs, and how to employ best logging practices that will come in handy during application troubleshooting. After this tutorial, you will be able to create a powerful error handling and logging system in your PHP application.


Prerequisites

  • Ubuntu 20.04 distribution including the non-root user with sudo access.
  • PHP installed.

Step 1 — Configuring Logs

PHP can be configured to display and log error output. It is recommended to review the following settings as you might want to make some changes. By default, PHP configuration files are located in the directory /etc/php/7.4/apache2/php.ini.

The /7.4/ directory might be named differently on your system as it represents your current version of PHP.  The full path to the php.ini configuration file might also be different depending on your operating system and a web server. In this case, we are using Ubuntu 20.04 and Apache web server with PHP 7.4.3.

Viewing Configuration File

To view the php.ini configuration file, run the following command:

sudo nano /etc/php/7.4/apache2/php.ini

Important Configuration Directives

These are the most important configuration directives. We highly recommend changing the values of some of them as it makes logging much easier.

  • display_errors: defines whether errors will be shown in the output. The default value is 1 but it's recommended to change it to 0 for a web-facing server.
  • display_startup_errors: defines whether to display PHP startup sequence errors. Default value is 0.
  • error_log: defines the path to the log file where the errors will be outputted (if the log_errors is 1).
  • error_reporting: sets the minimum error reporting level. The default value is null. We recommend changing it to E_ALL.
  • log_errors: toggles error logging. The default value is 0. The recommended value is 1.
  • log_errors_max_length: sets maximum length of error log. Set 0 to no limit or leave the default value.
  • track_errors: if set to 1, the last error is stored in the $php_errormsg variable.

Changing Configuration Directives

The first way to change configuration directives is to open the php.ini configuration file in the text editor and change it manually.

The second way is to change it using PHP. The following example shows how to view and change the values of specific directives.

/*
Viewing value of the configuration directive 
'log_errors' using ini_get()
*/
$log_errors = ini_get("log_errors");
echo "log_errors = " . $log_errors;

/*
Changing value of the configuration directive
'log_errors' using ini_set()
*/
ini_set("log_errors",true);

Step 2 — Finding Logs

Error logs will be outputted to the log file only if you set the value of log_errors directive to 1. If you leave the default value of error_log, errors will be outputted to these locations:

  • /var/log/apache2/error.log for Apache web server and Ubuntu operating system.
  • /var/log/nginx/error.log for Nginx web server and Ubuntu operating system.
  • XAMPP_installation_directory/apache/logs/error.log for XAMPP developer environment.

Otherwise, errors will be outputted to a custom log file.


Step 3 — Logging in PHP

Now, when we have the necessary theory behind us, let's see some examples and best practices of logging in PHP.

PHP comes with a variety of functions we can use for logging. The following examples show functions that come with native PHP, but if you are looking for more efficient tools, you may also look at some PHP logging libraries as they offer more functionality.

Logging To A File

The error_log() function sends a message to the current log file. You can use it with only one parameter which is the custom message you want to send as shown in the following example:

error_log("I've just added an entry to the log file!");

This will create a new entry in the log file with the text message you've entered as a parameter of the function. Entry will look like this:

Output:
[15-Apr-2021 10:23:18 Europe/Berlin] I've just added an entry to the log file!

Manually Triggering An Error

In PHP, you can also manually trigger a custom error using trigger_error() function. This function takes two parameters. The first parameter is the actual error message you want to send and the second parameter is the type of the error (E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE) where E_USER_NOTICE is the default value.

trigger_error('My custom error', E_USER_WARNING);

The code above will output the following entry to the currently configured log file.

Output:
[15-Apr-2021 10:33:29 Europe/Berlin] PHP Warning:  My custom error in /html/index.php on line 2

For example, you can use this function to track when the user enters a wrong password.

if($enteredPassword != $userPassword){
		/* Custom error */
    trigger_error("User $user entered wrong password",E_USER_ERROR);
    /* Code that handled wrong password ... */
}

Logging With Syslog

You can also log errors by sending them directly to the default system log. To do so, you need to open the log with the openlog() function where you specify the error prefix, option, and facility. Then you send the message with the syslog() function where you specify the priority of the log and actual message. then you close the log with the closelog() function.

openlog("MyAppPrefix", LOG_PID | LOG_PERROR,LOG_USER);
syslog(LOG_ERR, "Custom error message");
closelog();

Step 4 — Formatting Logs

When sending a message to the log, it's highly recommended and very useful to include important information that can help with the subsequent debugging such as a time and when the error occurred, the URL where the error occurred, and any other additional data that can help determine the problem.

Error Types

PHP comes with a variety of error types that identifies the severity of an error. These error types are integer values, but there are predefined constants for them. Here are some of them, for the full list visit official PHP documentation.

  • E_ERROR: a fatal error that causes script termination (e.g. Calling non-existent function, out of memory)
  • E_WARNING: run-time warning that doesn't terminate script execution (e.g. using an undefined variable in an expression)
  • E_PARSE: compile-time parse error
  • E_NOTICE: run-time notice due to error in the code
  • E_USER_ERROR: user-generated error
  • E_USER_WARNING: user-generated warning
  • E_USER_NOTICE: user-generated notice
  • E_STRICT: run-time notice

Step 5 — Logging Using JSON

Logging user action is very resourceful and helpful for application development as you can track what functionalities users use the most or which steps they took to encounter the error. These pieces of information can be used to improve user experience and help to track down bugs in the code.

That requires collecting and logging many pieces of information and values. This can be made easier by using JSON. It's an acronym for Java-script Object Notation. Using JSON you can transform a string (in JSON format) to stdClass object or array and back from object or array to string. This allows you to store logs in the JSON formatted string and then when it's needed you can convert all logged values and information to a single object or array.

For conversion to JSON formatted string use json_encode(). For conversion from JSON formatted string use json_decode().

Example Of Logging Using JSON

//Data that will be logged
$dataToLog = array(
    "errMsg" => "Requested page does not exist",
    "errCode" => 400,
    "url" => "my-application.com/non-existent-page",
    "userLoggedIn" => true,
    "userName" => "alice",
    "time" => date("Y-m-d H:i:s")
);
//Conversion
$stringToLog = json_encode($dataToLog);
//Logging
error_log($stringToLog);

//Print the result
echo $stringToLog;

This will print and store this string in the log:

Output:
{"errMsg":"Requested page does not exist","errCode":400,"url":"my-application.com\\/non-existent-page","userLoggedIn":true,"userName":"alice","time":"2021-04-15 12:30:00"}

Reading JSON Log

//Logged data
$jsonLog = '{
    "errMsg":"Requested page does not exist",
    "errCode":400,
    "url":"my-application.com\\/non-existent-page",
    "userLoggedIn":true,
    "userName":"alice",
    "time":"2021-04-15 12:30:00"
}';

//conversion
$result = json_decode($jsonLog);

//Print result
echo $result->errMsg . "<br>" . $result->errCode . "<br>" .
     $result->url . "<br>" . $result->userLoggedIn . "<br>" .
     $result->userName . "<br>" . $result->time . "<br>";

This will print result to the screen

Output:
Requested page does not exist
400
my-application.com/non-existent-page
1
alice
2021-04-15 12:30:00

Step 6 — Creating Exceptions

When PHP started supporting OOP (Object-oriented programming), it introduced Exception. It's a great tool to handle errors on the code layer and subsequent logging. Exceptions are handled by throw / catch / finally blocks in the code.

Throwing Exceptions

In the try block is code that can throw an exception when the error occurs. This exception will be caught and processed in the catch block. You may also use the finally block that will be executed even when the exception is not thrown.

try{
		/* Some code above */

    if(true) //Error has occurred here
        throw new Exception("Error has occurred");

		/* Some code below */
}
catch(Exception $e){
    //Catching and handling exception
    echo $e->getMessage();
    //Error logging might follow
}

This will print error message on the screen:

Output:
Error has occurred

Custom Exception

You can also create your custom exception for specific errors and problems you may encounter in your application. In this example, we will create an exception for handling division by zero.

Custom exception class with the constructor:

class DivisionByZeroException extends Exception{

    public function __construct($msg){
        parent::__construct($msg);
    }

}

Code that throws this exception:

try{
    $a = 4;
    $b = 0;

    if($b != 0){
        $c = $a / $b;
    }
    else{
        throw new DivisionByZeroException("You can't divide by zero!");
    }
}
catch(Exception $e){
    //Catching and handling exception
    echo $e->getMessage();
    //Error logging might follow
}

This will print custom error message:

Output:
You can't divide by zero!

SPL Exceptions

Speaking of custom exceptions, PHP has a built-in library called SPL (Standard PHP library) that offers a number of predefined exception classes for common errors that we can use in our code. Here is the full list of the exception classes available in the SPL.

  • BadFunctionCallException: thrown if a callback refers to an undefined function or if some arguments are missing.
  • BadMethodCallException: thrown if a callback refers to an undefined method or if some arguments are missing.
  • DomainException: thrown if a value does not adhere to a defined valid data domain
  • InvalidArgumentException: thrown if an argument is not of the expected type.
  • LengthException: thrown if a length is invalid.
  • LogicException: represents error in the program logic.
  • OutOfBoundsException: thrown if a value is not a valid key.
  • OutOfRangeException: thrown when an illegal index was requested.
  • OverflowException: thrown when adding an element to a full container.
  • RangeException: thrown to indicate range errors during program execution.
  • RuntimeException: thrown if an error which can only be found on runtime occurs.
  • UnderflowException: thrown when performing an invalid operation on an empty container, such as removing an element.

Conclusion

You learned where logs are stored, how to change configuration directives and how to store logs using PHP. You also learned how to log user's activities using JSON and handle errors using Exception. Combining all the knowledge above you are able to create powerful error handling and logging system in your PHP application that will help you to track down bugs in your code and develop better application.