A good way to create Logs in PHP

A good way to create Logs in PHP

Hello everyone :)

Today we’re gonna go over Logging in PHP as nested Class.

With the introduction of PHP5 we now get the opportunity to use PHP as an Object Oriented Programming Language. What this means is, and in a very short way, that we can now have a more reliable, faster and safer way to program in PHP.

OO programming brings abstraction, encapsulation, inheritance, and polymorphism so, therefore, PHP also does.

  • Abstraction – Allows the programmer to declare abstract methods that can be instantiated in a class and declared by an object.
  • Encapsulation – Gives us the ability to protect which ever data, inside a class, by using protected methods like private classes ou functions.
  • Inheritance – We can now create tree like class structure where the lower levels on the tree are childes of the level above. (in this Logging example we’re going to be using this methodology)
  • Polymorphism – Using this feature we’re able to declare a given class and use his children methods even tho we have declared parent class without the need to redeclare them.

First of all we’re going to go over the basic structure of our Log class.

interface iLogs
{
 public function insereLog($entrada,$tipo); 
}

We declared an interface. This way the application that will be calling this class is able to read its available content without the need to directly access the class internally.

Note: Methods that are declared on the interface need to be equal to the ones declared inside the class itself (either method name or number of arguments required). Otherwise you’ll get a fatal error from PHP.

Next step takes us to declaration of the class itself.

class Logs implements iLogs
{
 const VERBOSE = false;
 // Max size for log compression -> 1MB
 private static $max_size = 1024000;
 // Folder in your root where the logs will be stored
 private static $cwd = 'logs/';

As you can see, we’re telling class Logs that it should implement interface iLogs that we previously declared.

Every class needs a constructor and a destructor. Basically what should and should not be done when we initialize the class.

 public function __construct() {
  if (!defined('LOG_FILE_ADMIN')){
   define('LOG_FILE_ADMIN','logAdmin.log');
  }
  if (!defined('LOG_FILE_CLIENTES')){
   define('LOG_FILE_CLIENTES','logClientes.log');
  }
  if (!defined('LOG_FILE_WEBSERVICES')){
   define('LOG_FILE_WEBSERVICES','logWebServices.log');
  }
  self::preparaDirectorias();
  self::preparaLogs();
 }
 public function __destruct() {
  
 }

__construct and __destruct use “__” since they are native to PHP5 and are automatically called when a class is instantiated.

When the class is built we want to create some constants used throughout the class and we want to check if everything is according to the file structure required.
We also use self to address the actual instantiated class and :: in place of -> because we’ll be calling them in static mode (no data can be changed).

Our destruct class is empty since there is nothing to be done when the class is destroyed.

Note: Normally you can use this function for a DB connection and destruct can close the connection.

 private function preparaDirectorias() {
  chdir('./');
  if(!is_dir("logs")) {
   if(!mkdir('logs'))
    throw new Exception ("It was no possible to create 'logs' dir"); 
  }
  if(!is_dir(self::$cwd."logs_arquivo")) {
   if(!mkdir(self::$cwd.'logs_arquivo'))
    throw new Exception ("It was no possible to create 'logs_arquivo' dir");
  }
 }

We attempt to check if folder structure is properly created, if not we create it.

 private function preparaLogs() {
  if (!file_exists(self::$cwd.LOG_FILE_ADMIN)) { 
   if(!fopen(self::$cwd.LOG_FILE_ADMIN , 'w'))
    throw new Exception("It was not possible to create '".LOG_FILE_ADMIN );
  } 
  if(filesize(self::$cwd.LOG_FILE_ADMIN) > self::$max_size) {
   self::compressbz2(LOG_FILE_ADMIN);
   unlink(self::$cwd.LOG_FILE_ADMIN);
   if(!fopen(self::$cwd.LOG_FILE_ADMIN , 'w'))
    throw new Exception("It was not possible to create '".LOG_FILE_ADMIN );
  }
  if (!file_exists(self::$cwd.LOG_FILE_CLIENTES )) { 
   if(!fopen(self::$cwd.LOG_FILE_CLIENTES , 'w'))
    throw new Exception("It was not possible to create '".LOG_FILE_CLIENTES );
  }
  if(filesize(self::$cwd.LOG_FILE_CLIENTES) > self::$max_size) {
   self::compressbz2(LOG_FILE_CLIENTES);
   unlink(self::$cwd.LOG_FILE_CLIENTES);
   if(!fopen(self::$cwd.LOG_FILE_CLIENTES , 'w'))
    throw new Exception("It was not possible to create '".LOG_FILE_CLIENTES );
  }
  if (!file_exists(self::$cwd.LOG_FILE_WEBSERVICES)) { 
   if(!fopen(self::$cwd.LOG_FILE_WEBSERVICES, 'w'))
    throw new Exception("It was not possible to create '".LOG_FILE_WEBSERVICES);
  }
  if(filesize(self::$cwd.LOG_FILE_WEBSERVICES) > self::$max_size) {
   self::compressbz2(LOG_FILE_WEBSERVICES);
   unlink(self::$cwd.LOG_FILE_WEBSERVICES);
   if(!fopen(self::$cwd.LOG_FILE_WEBSERVICES, 'w'))
    throw new Exception("It was not possible to create '".LOG_FILE_WEBSERVICES);
  }
 }

We’re going to check the size of our logs. If they exceed $max_size then we compress them and create a new and fresh log file.

 private function compressbz2($file_name) {
  $file_pointer = fopen(self::$cwd.$file_name, "r");
  //Opens the file in readonly,
  //Check for permissions
  $file_read = fread($file_pointer, filesize(self::$cwd.$file_name));
  //Reads the content
  fclose($file_pointer);
  //Closes the file
  $actual = date("-Ymd-His");
  $filename = $file_name.$actual.".bz2";
  //Opens compression file for writing purpose
  $bz = bzopen(self::$cwd.'logs_arquivo/'.$filename, "w");
  //Writes the file
  bzwrite($bz, $file_read);
  //Closes the file
  bzclose($bz);
 }

A simple function for compressing files. I will not go into a lot of detail on this section. If you find it to be useful please contact me and I’ll make a post about file compression.

And finally we’re going to create the method that we call when using this class to add a log.

Note: As you probably noticed (or not :P ) all other methods (except construct and destruct) are private and therefore can only be called from within the class itself.

 public function insereLog($entrada,$tipo) {
  if($tipo == 1) {
   $fp = fopen(self::$cwd.LOG_FILE_ADMIN , 'a+');
   if(!$fp)
    throw new Exception("It was not possible to create '".LOG_FILE_ADMIN );
   else {
    fwrite($fp,'['.date("Y/m/d - H:i:s").'] '.$entrada."n");
    if(self::DEBUG)
     echo $entrada."n";
   }
  }
  elseif($tipo == 2) {
   $fp = fopen(self::$cwd.LOG_FILE_CLIENTES , 'a+');
   if(!$fp)
    throw new Exception("It was not possible to create '".LOG_FILE_CLIENTES );
   else {
    fwrite($fp,'['.date("Y/m/d - H:i:s").'] '.$entrada."n");
    if(self::DEBUG)
     echo $entrada."n";
   }
  }
  elseif($tipo == 3) {
   $fp = fopen(self::$cwd.LOG_FILE_WEBSERVICES , 'a+');
   if(!$fp)
    throw new Exception("It was not possible to create '".LOG_FILE_WEBSERVICES );
   else {
    fwrite($fp,'['.date("Y/m/d - H:i:s").'] '.$entrada."n");
    if(self::DEBUG)
     echo $entrada."n";
   }
  }
  fclose($fp);
 }

This logging class supports logs for 3 different types of systems; frontoffice (clients), backoffice (administrations) and webservices.

The way you call this function can be done in different ways, in this case, I’ll go over the Inheritance feature we talked before.

Imagine you have a class, lets say, foo

class foo {
 public function hello() {
  // I bet you never seen this before!!! hehe ^_^
  echo 'Hello World';
 }
}

What we can do is…

//Logs.class.php is your Log class we just did
require_once 'Logs.class.php';

class foo extends Logs {
 public function hello() {
  // I bet you never seen this before!!! hehe ^_^
  echo 'Hello World';
  Logs::insereLog("Mom, I think i just said Hello World. Log it please, thank you!",1);
 }
}

And voila, there you go :) your very own log system out of the box.

Feel free to report me any mistakes, suggestions or doubts you might have.

Have fun and be != be# {‘run remotly’ && Get['4_free'];}

10
Feb 2009
AUTHOR joseairosa
CATEGORY

Myself

COMMENTS 401 Comments