Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Logging design pattern in a Zend framework application

I'm building an application using Zend Framework. The application requires intensive logging for every action or function in the code.

so my code most of the time looks like this:

function SendMailsAction(){
      $logger->log('Started sending mails.')
...
...
...Some Code...
...
...

foreach ($mails as $mail){
  try{      
       $logger->log('Trying to send')
       $mail->send()
       $logger->log('Mail sent successfully.')
  }catch(Exception $e){
       $logger->log('Failed to send mail.')
      }      
 }

...
...
...Some Code...
...
...
       $logger->log('Finished sending mails.')
}

Sometimes I even have to log in 2 tables, so most of the logging code is doubled and the functions start to get complicated and long.

I use Zend framework's Zend_Log for logging so my problem is not the logging class itself, But it's how to separate the logging code from the code functionality itself and maintain separation of concerns.

Some people suggested Aspect Oriented Programming (AOP), but unfortunately AOP for PHP isn't acceptable for my costumer, so I'm looking for an Object Oriented solution or best practice.

Note:

Just to make things clear my problem isn't how to use Zend_Log, but how to add logging to my application code in general.

like image 520
Songo Avatar asked Feb 02 '23 08:02

Songo


1 Answers

Sometimes I even have to log in 2 tables, so most of the logging code is doubled and the functions start to get complicated and long.

It'll be long. If your code does a lot of logging, it will be long, as it will have to log, and each line action it logs, will mean there's a line in your code. It shouldn't, however, be complicated in any case as logging is one of the most straightforward thing you can do. What worries me, is that you mention "sometimes I even have to log in 2 tables". In my book, one, two, five, sixty or one thousand tables is performed by one line. Code is not doubled for each logger. If you're copy-pasting a line, and changing $log to $log2, you're clearly doing it wrong (tm).

Some people suggested Aspect Oriented Programming (AOP), but unfortunately AOP for PHP isn't acceptable for my costumer, so I'm looking for an Object Oriented solution or best practice.

It is nice, AOP. It has downsides though; as with the debug_backtrace approach, there's a heavy performance hit. That, plus the code becomes increasingly more "magical" in that it does things that aren't clear when you're looking at the code itself. That increases the time you're debugging your application.

My $0.02? First of all, don't repeat yourself: one log entry per action should be enough. Use flexible loggers that can be attached to certain classes at runtime. Decide whether or not to actually log the message in the logger, based on "severity" or "type". All in all, just implement the Observer pattern:

<?php

namespace Foo;

class MailService {
    public function attach( Observes $observer ) {
        $this->observers[] = $observer;
    }

    public function notify( $message, $type = 'notice' ) {
        foreach( $this->observers as $observer ) {
            $observer->notify( $message, $type );
        }
    }

    public function sendMail( ) {
        $this->notify( 'Started sending mails', 'debug' );
        $mails = array( );
        foreach( $mails as $mail ) {
            try {
                $this->notify( 'Trying to send', 'debug' );
                $mail->send( );
                $this->notify( 'Mail sent succesfully', 'debug' );
            }
            catch( Exception $e ) {
                $this->notify( 'Failed to send mail', 'notice' );
            }
        }
        $this->notify( 'Finished sending mail', 'debug' );
    }
}

interface Observes {
    public function notify( $message, $type = 'notice' );
}

abstract class Logger implements Observes {
    protected $types = array(
        'debug' => 0,
        'notice' => 1,
        'warning' => 2,
        'error' => 3
    );

    protected function code( $type ) {
        return isset( $this->types[$type] ) ? $this->types[$type] : 0;
    }
}

class FileLogger extends Logger implements Observes {

    public function __construct( $filename ) {
        $this->filename = $filename;
    }

    /**
     * @todo replace the method body with a call to, say, file_put_contents.
     */
    public function notify( $message, $type = 'notice' ) {
        if( $this->code( $type ) > $this->code( 'notice' ) ) { // only for warning and error.
            echo $message . "\n";
        }
    }


}

class DebugLogger extends Logger implements Observes {
    public function notify( $message, $type = 'notice' ) {
        if( $this->code( $type ) === $this->code( 'debug' ) ) { // only show "debug" notices.
            echo $message . "\n";
        }
    }
}


$service = new MailService( );
$service->attach( new FileLogger( 'yourlog.txt' ) );
$service->attach( new DebugLogger( ) );
$service->sendMail( );
like image 124
Berry Langerak Avatar answered Feb 05 '23 15:02

Berry Langerak