Monday, October 19, 2015

error handling in PHP

Hello


Motivation
The default error handling in PHP is very simple. An error message with filename, line number and a message describing the error is sent to the browser. 
What if you want to save this error also to a log file ? or catch warning\error\exception ?

Creating Custom Error Handler

Creating a custom error handler is quite simple. We simply create a special function that can be called when an error occurs in PHP.
This function must be able to handle a minimum of two parameters (error level and error message) but can accept up to five parameters (optionally: file, line-number, and the error context):

Syntax

error_function(error_level,error_message,
error_file,error_line,error_context)
ParameterDescription
error_levelRequired. Specifies the error report level for the user-defined error. Must be a value number. See table below for possible error report levels
error_messageRequired. Specifies the error message for the user-defined error
error_fileOptional. Specifies the filename in which the error occurred
error_lineOptional. Specifies the line number in which the error occurred
error_contextOptional. Specifies an array containing every variable, and their values, in use when the error occurred

Error Report levels

These error report levels are the different types of error the user-defined error handler can be used for:
ValueConstantDescription
2E_WARNINGNon-fatal run-time errors. Execution of the script is not halted
8E_NOTICERun-time notices. The script found something that might be an error, but could also happen when running a script normally
256E_USER_ERRORFatal user-generated error. This is like an E_ERROR set by the programmer using the PHP function trigger_error()
512E_USER_WARNINGNon-fatal user-generated warning. This is like an E_WARNING set by the programmer using the PHP function trigger_error()
1024E_USER_NOTICEUser-generated notice. This is like an E_NOTICE set by the programmer using the PHP function trigger_error()
4096E_RECOVERABLE_ERRORCatchable fatal error. This is like an E_ERROR but can be caught by a user defined handle (see also set_error_handler())
8191E_ALLAll errors and warnings (E_STRICT became a part of E_ALL in PHP 5.4)


Now lets create a function to handle errors: 
function customError($errno, $errstr) {
  echo "<b>Error:</b> [$errno] $errstr<br>";
  echo "Ending Script";
  die();
}
The code above is a simple error handling function. When it is triggered, it gets the error level and an error message. It then outputs the error level and message and terminates the script.

Now that we have created an error handling function we need to decide when it should be triggered.

Set Error Handler
function customError($errno, $errstr,$error_file,$error_line) 
{
    echo "<b>Error:</b> $error_file $error_line  [$errno] $errstr";
}
//set error handler
set_error_handler("customError");
//trigger error
echo($test);

this output :

Error: E:\nathan\PHP\VS\PHPWebProject2\PHPWebProject2\index.php 17 [8] Undefined variable: test


Using log file \ Email
you can send the error to a log_file using error_log

The error_log() function sends an error message to a log, to a file, or to a mail account.


error_log(message,type,destination,headers);


ParameterDescription
messageRequired. Specifies the error message to log
typeOptional. Specifies where the error message should go. Possible values:
  • 0 - Default. Message is sent to PHP's system logger, using the OS' system logging mechanism or a file, depending on what the error_log configuration is set to in php.ini
  • 1 - Message is sent by email to the address in the destination parameter
  • 2 - No longer in use (only available in PHP 3)
  • 3 - Message is appended to the file specified in destination
  • 4 - Message is sent directly to the SAPI logging handler
destinationOptional. Specifies the destination of the error message. This value depends on the value of thetype parameter
headersOptional. Only used if the type parameter is set to 1. Specifies additional headers, like From, Cc, and Bcc. Multiple headers should be separated with a CRLF (\r\n)


function customError($errno, $errstr,$error_file,$error_line) 
{
    // --- error to display
    echo "<b>Error:</b>  [$errno] $errstr";
    // --- error to PHP system log
    $error = $error_file." ".$error_line." [".$errno."] ". $errstr;
    error_log($error,0); // send to php error file
   error_log($error,1,"natankrasney@gmail.com","From: webmaster@example.com");
}

//set error handler
set_error_handler("customError");
//trigger error
echo($test);

this will output to the browser :

and to the php error file (pending php.ini)



Exception Handling
the set_exception_handler() function sets a user-defined function to handle all uncaught exceptions.
<?php
function myException($exception) {
  echo "<b>Exception:</b> " . $exception->getMessage();
}

set_exception_handler('myException');

throw new Exception('Uncaught Exception occurred');
?>
The output of the code above should be something like this:
Exception: Uncaught Exception occurred

it is possible to log the exception as follows :

function customError($errno, $errstr,$error_file,$error_line)
{
    // --- error to display
    echo "<b>Error:</b>  [$errno] $errstr";
    // --- error to PHP system log
    $error = $error_file." ".$error_line." [".$errno."] ". $errstr;
    // send to php error file
    error_log($error,0); // send to php error file
    // send to php error to e-mail but need configuration
    //error_log($error,1,"natankrasney@gmail.com","From: webmaster@example.com");
}


function exception_handler($exception) {
    trigger_error( "Uncaught exception: ".$exception->getMessage()."\n");
}

set_exception_handler('exception_handler');


//set error handler
set_error_handler("customError");

$error = 'Always throw this error';
throw new Exception($error);

//trigger error
echo($test);



References


Nathan

Sunday, October 18, 2015

Using Logs in PHP - Log4PHP

Hello

Logging is a must for development and production enviroments.
Log4PHP is a logger for php (log4net exist for .net , log4j for java , .....)

download the sources from https://logging.apache.org/log4php/download.html
install src/main/php into your project. 


Sample code 1

  1. include('log4php/Logger.php');
  2. $logger = Logger::getLogger("main");
  3. $logger->info("This is an informational message.");
  4. $logger->warn("I'm not feeling so good...");

remark : here i copy it to a folder name Log4php

this result in :



Sample code 2


First, create a configuration file named config.xml containing:
  1. <configuration xmlns="http://logging.apache.org/log4php/">
  2. <appender name="myAppender" class="LoggerAppenderFile">
  3. <param name="file" value="myLog.log" />
  4. </appender>
  5. <root>
  6. <level value="WARN" />
  7. <appender_ref ref="myAppender" />
  8. </root>
  9. </configuration>
This configuration file does the following:
  • line 2: Creates an appender named myAppender using appender class LoggerAppenderFile which is used for logging to a file.
  • line 3: Sets the file parameter, which tells the appender to which file to write.
  • line 6: Sets the root logger level to WARN. This means that logging requests with the level lower thanWARN will not be logged by the root logger.
  • line 7: Links myAppender to the root logger so that all events recieved by the root logger will be forwarded to myAppender and written into the log file.

Put this file at the same directory as your PHP project


To try it out, run the following code:
  1. // Insert the path where you unpacked log4php
  2. include('log4php/Logger.php');
  3.  
  4. // Tell log4php to use our configuration file.
  5. Logger::configure('config.xml');
  6.  
  7. // Fetch a logger, it will inherit settings from the root logger
  8. $log = Logger::getLogger('myLogger');
  9.  
  10. // Start logging
  11. $log->trace("My first message."); // Not logged because TRACE < WARN
  12. $log->debug("My second message."); // Not logged because DEBUG < WARN
  13. $log->info("My third message."); // Not logged because INFO < WARN
  14. $log->warn("My fourth message."); // Logged because WARN >= WARN
  15. $log->error("My fifth message."); // Logged because ERROR >= WARN
  16. $log->fatal("My sixth message."); // Logged because FATAL >= WARN
This result in myLog.log created as follows :




Sample code 3
This example covers named loggers, layouts and best practices in object-oriented programming.
Create a configuration file named config.xml with the following content:
  1. <configuration xmlns="http://logging.apache.org/log4php/">
  2.  
  3. <appender name="myConsoleAppender" class="LoggerAppenderConsole" />
  4. <appender name="myFileAppender" class="LoggerAppenderFile">
  5. <layout class="LoggerLayoutPattern">
  6. <param name="conversionPattern" value="%date [%logger] %message%newline" />
  7. </layout>
  8. <param name="file" value="myLog.log" />
  9. </appender>
  10.  
  11. <logger name="Foo">
  12. <appender_ref ref="myFileAppender" />
  13. </logger>
  14. <root>
  15. <level value="DEBUG" />
  16. <appender_ref ref="myConsoleAppender" />
  17. </root>
  18. </configuration>
The configuration defines two appenders: one writes to the console, and the other to a file.
The console appender doesn't have a layout defined, so it will revert to default layout (LoggerLayoutSimple). The file appender uses a different layout (LoggerLayoutPattern) which will result in different formatting of the logging events.
The console appender is linked to the root logger. The file appender is linked to the logger named Foo, however Foo also inherits appenders from the root logger (in this case the console appender). This means that logging events sent to the Foo logger will be logged both to the console and the file.
Consider the following code snippet:
  1. // Include and configure log4php
  2. include('log4php/Logger.php');
  3. Logger::configure('config.xml');
  4.  
  5. /**
  6. * This is a classic usage pattern: one logger object per class.
  7. */
  8. class Foo
  9. {
  10. /** Holds the Logger. */
  11. private $log;
  12.  
  13. /** Logger is instantiated in the constructor. */
  14. public function __construct()
  15. {
  16. // The __CLASS__ constant holds the class name, in our case "Foo".
  17. // Therefore this creates a logger named "Foo" (which we configured in the config file)
  18. $this->log = Logger::getLogger(__CLASS__);
  19. }
  20.  
  21. /** Logger can be used from any member method. */
  22. public function go()
  23. {
  24. $this->log->info("We have liftoff.");
  25. }
  26. }
  27.  
  28. $foo = new Foo();
  29. $foo->go();
The output to file is as follows :

code for sample code 3 



Sample code 4
use rolling file with file name and line number
this appender fills the first file until maxFileSize [MB] then it copy the file to <file_name>1, after the second time it is to  <file_name>2 and this is maxBackupIndex times. after this the file is copy to  <file_name>1 and it start over again

Config.xml
<configuration xmlns="http://logging.apache.org/log4php/">
  <appender name="myAppenderRollingFile" class="LoggerAppenderRollingFile">
        <layout class="LoggerLayoutPattern">
            <param name="conversionPattern" value="%date %file : %line [%logger] %message%newline" />
        </layout>
        <param name="file" value="E:\wamp\www\TryLogger4PHP\file.log" />
        <param name="maxFileSize" value="1MB" />
        <param name="maxBackupIndex" value="5" />
    </appender>
 
<root>
        <level value="ERROR" />
        <appender_ref ref="myAppenderRollingFile" />
    </root>

  </configuration>

remarks

  • one must use full path for file due to Log4PHP bug
  • maxFileSize - max size of file
  • maxBackupIndex - number of backup file


php file
<?php
// Include and configure log4php
include('log4php/Logger.php');


// Tell log4php to use our configuration file.

Logger::configure('config.xml');

// Fetch a logger, it will inherit settings from the root logger

$log = Logger::getLogger('myLogger');


// Start logging

$log->trace("My first message.");   // Not logged because TRACE < WARN

$log->debug("My second message.");  // Not logged because DEBUG < WARN

$log->info("My third message.");    // Not logged because INFO < WARN

$log->warn("My fourth message.");   // Logged because WARN >= WARN

$log->error("My fifth message.");   // Logged because ERROR >= WARN

$log->fatal("My sixth message.");   // Logged because FATAL >= WARN

?>

output :

code for sample code 4




References :



Nathan