2012-02-16 2 views
9

Я создаю приложение с использованием Zend Framework. Приложение требует интенсивного ведения журнала для каждого действия или функции в коде.Кадровый шаблон проектирования в приложении Zend Framework

мой код большую часть времени выглядит следующим образом:

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.') 
} 

Иногда я даже войти в 2 таблицы, так что большая часть кода регистрации удваивается и функции начинают усложняться и долго.

Я использую Zend framework Zend_Log для регистрации, поэтому моя проблема не является самим протоколом ведения журнала. Но это то, как отделить код регистрации от самой функции кода и поддерживать разделение проблем.

Некоторые люди предлагали аспектно-ориентированное программирование (АОП), но, к сожалению, АОП для PHP не подходит для моего заказчика, поэтому я ищу объектно-ориентированное решение или лучшую практику.

Примечание:

Просто, чтобы сделать вещи ясно, моя проблема заключается не в том, чтобы использовать Zend_Log, но как добавить протоколирование в мой код приложения в целом.

+0

Что вы думаете, что ваш код должен выглядеть? Есть ли какой-то желательный бит кода? – akond

+0

@akond well Я думал о «прикреплении» функций журнала к определенным функциям в моделях или контроллерах. что-то вроде регистрации функций для работы в определенных точках кода. Если любой из них даже применим в PHP, конечно. – Songo

+0

Хороший вопрос, и я думаю, что ZF2 будет обрабатывать эту проблему гораздо шире с помощью [EventManager] (http://mwop.net/blog/266-Using-the-ZF2-EventManager#toc_1.6). –

ответ

5

Иногда мне даже приходится регистрироваться в 2 таблицах, поэтому большая часть кода регистрации удваивается, и функции начинают усложняться и длиться.

Это будет долго. Если ваш код делает много протоколирования, он будет быть длинным, так как он должен будет регистрироваться, и каждое действие строки, которое он регистрирует, будет означать, что в вашем коде есть строка.Однако он не должен быть сложным в любом случае, поскольку регистрация является одной из самых простых вещей, которую вы можете сделать. Меня беспокоит то, что вы упоминаете «иногда мне даже приходится входить в 2 таблицы». В моей книге одна, две, пять, шестьдесят или одна тысяча столов выполняется одна линия. Код не удваивается для каждого регистратора. Если вы копируете строку и меняете $log на $log2, вы явно делаете это неправильно (tm).

Некоторые люди предлагали аспектно-ориентированное программирование (АОП), но, к сожалению, АОП для PHP не подходит для моего заказчика, поэтому я ищу объектно-ориентированное решение или лучшую практику.

Хорошо, АОП. У него есть минусы; как и при использовании метода debug_backtrace, наблюдается сильный удар по производительности. Это плюс код становится все более «волшебным» в том, что он делает вещи, которые не ясны, когда вы смотрите на сам код. Это увеличивает время отладки вашего приложения.

Мои $ 0,02? Прежде всего, не повторяйте себя: достаточно одной записи в журнале за действие. Используйте гибкие регистраторы, которые можно привязать к определенным классам во время выполнения. Определите, действительно ли зарегистрировать сообщение в регистраторе на основе «серьезности» или «типа». В общем, просто реализовать шаблон Observer:

<?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(); 
+0

+1 для рабочего кода и отличное объяснение.Просто интересно, почему, почему вы сделали абстрактный класс реализованным интерфейсом в 'abstract class Logger реализует Observes'? – Songo

+0

@Songo В основном потому, что я уже написал интерфейс, а затем обнаружил, что оба имеют общее поведение, которое можно так же легко абстрагировать до родительского класса. Но, если вы когда-нибудь напишите Observer, который не регистрирует материал, вы можете найти интерфейс полезным в любом случае. –

1

вы знаете, что журнал Зенд может иметь более одного писателя http://framework.zend.com/manual/en/zend.log.writers.html#zend.log.writers.compositing

его простой, как создание класса одной функции

class logger { 
    public function log ($value , $type , $bothlogger = false){ 
     $writer1 = new Zend_Log_Writer_Stream('/path/to/first/logfile'); 
     $writer2 = new Zend_Log_Writer_Stream('/path/to/second/logfile'); 

     $logger = new Zend_Log(); 
     $logger->addWriter($writer1); 

     if($bothlogger === true){ 
     $logger->addWriter($writer2); 
     } 
     // goes to both writers 
     $logger->info('Informational message'); 

     return true; 
    } 

} 

, конечно, вы можете изменить этот образец, чтобы быть быстрее, со многими способами, но он должен объяснить идею

+0

На самом деле моя проблема заключается не в том, чтобы использовать класс Zend_Log, а в том, чтобы мой код был чистым и поддерживал разделение из-за проблем в используемых классах. – Songo

+0

+1 для напоминания о том, что я мог бы подключить к журналу несколько писателей. :) – Songo

+0

Я заметил, что вы включили регистратор в функцию. Я попытался сделать это в области класса, и это не удалось. Это нормально? Я создаю для каждой функции новую запись? – Erik

4

Если вы не хотите использовать какие-либо внешние инструменты, вы можете написать какую-то обсерверу наблюдателя около debug_backtrace, которая проходит через обратную трассировку и сравнивает все вызовы функций с arra y, предоставленной оберткой, и, если это попадание, записывает соответствующее сообщение журнала с текстом настраиваемого сообщения. Это было бы полное разделение кода, где вам просто нужно было бы запустить этот класс наблюдателя в конце каждого скрипта.

Что касается примера, я думаю, что все, что вам нужно, приведено в примерах руководства PHP. Тем не менее, здесь есть некоторые псевдо-код, чтобы осветить то, что я имею в виду:

//use register_shutdown_function to register your logger function 

function scanTrace(Zend_Log $logger, array $eventMap) 
{ 
    $trace = array_reverse(debug_backtrace()); 

    foreach ($trace as $step) 
    { 
     //1. extract the needed info 
     //2. check if the event is in your eventMap 
     //3. if yes, log it 
    } 
} 

eventMap уже должен содержать сообщение, которое вы хотите, чтобы войти для каждого события.

Если вы не против использования внешнего инструмента (что, я думаю, это лучший вариант), вы можете работать с xdebug и WebGrind или аналогичным.

Btw: Вас может заинтересовать monitorix, который является расширением Zend_Log с большим количеством удобного автоматического ведения журнала в таблице db (например, медленное ведение журнала запросов, ошибка php и регистрация исключений, ведение журнала ошибок javascript).

+0

Не могли бы вы подробнее объяснить первую часть вашего ответа ?. Не могли бы вы разместить некоторые примеры кода об использовании 'debug_bactrace', пожалуйста? – Songo

+0

+1 для monitorix, кажется, хорошо – tawfekov

+1

Я обновил ответ, чтобы дать вам пример того, что я имею в виду. – markus

2

Я имею регистрацию в моей службе Zend2 с помощью моей Go! AOP PHP библиотеки. Он достаточно быстрый и позволяет мне отлаживать исходный код с помощью XDebug в режиме разработки. Тем не менее, это только бета-версия, помните!

use Go\Aop\Aspect; 
use Go\Aop\Intercept\MethodInvocation; 
use Go\Lang\Annotation\After; 
use Go\Lang\Annotation\AfterThrowing; 
use Go\Lang\Annotation\Before; 
use Go\Lang\Annotation\Around; 

/** 
* Logging aspect 
*/ 
class LoggingAspect implements Aspect 
{ 

    /** 
    * @var Zend\Log\Logger 
    */ 
    protected $logger = null; 

    /** 
    * Constructs a logging aspect 
    */ 
    public function __construct() 
    { 
     $logger = new Zend\Log\Logger; 
     $writer = new Zend\Log\Writer\Stream('php://output'); 

     $logger->addWriter($writer); 
     $this->logger = $logger; 
    } 

    /** 
    * Method that will be called before real method 
    * 
    * @param MethodInvocation $invocation Invocation 
    * @Before("execution(public ClassName->*(*))") 
    */ 
    public function beforeMethodExecution(MethodInvocation $invocation) 
    { 
     $msg = 'Before: '. $this->formatMessage($invocation); 
     $this->logger->log(Zend\Log\Logger::INFO, $msg); 
    } 

    /** 
    * Method that will be called after throwing an exception in the real method 
    * 
    * @param MethodInvocation $invocation Invocation 
    * @AfterThrowing("execution(public ClassName->*(*))") 
    */ 
    public function afterThrowingMethodExecution(MethodInvocation $invocation) 
    { 
     $msg = 'After throwing: '. $this->formatMessage($invocation); 
     $this->logger->log(Zend\Log\Logger::ERR, $msg); 
    } 

    /** 
    * Format a message from invocation 
    * 
    * @param MethodInvocation $invocation 
    * @return string 
    */ 
    protected function formatMessage(MethodInvocation $invocation) 
    { 
     $obj = $invocation->getThis(); 
     return is_object($obj) ? get_class($obj) : $obj . 
      $invocation->getMethod()->isStatic() ? '::' : '->' . 
      $invocation->getMethod()->getName() . 
      '()' . 
      ' with arguments: ' . 
      json_encode($invocation->getArguments()) . 
      PHP_EOL; 
    } 
} 

`` `

+0

+1 Это выглядит очень многообещающе. – Songo

+0

Ссылка на официальный сайт: http://go.aopphp.com/ – lisachenko

Смежные вопросы