Шаблон дизайна журнала в приложении Zend Framework - PullRequest
9 голосов
/ 16 февраля 2012

Я создаю приложение, используя 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 для ведения журналов, поэтому моя проблема не в самом классе журналирования, а в том, как отделить код журналирования от самой функциональности кода и поддерживать разделение задач.

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

Примечание:

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

Ответы [ 4 ]

5 голосов
/ 16 февраля 2012

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

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

Некоторые люди предлагают Аспектно-ориентированное программирование (AOP), но, к сожалению, AOP для 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( );
4 голосов
/ 16 февраля 2012

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

Что касается примера, я думаю, что все, что вам нужно, это примеры из руководства по 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 или аналогичными.

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

2 голосов
/ 25 ноября 2012

Я захожу в свой сервис 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;
    }
}

`` `

1 голос
/ 16 февраля 2012

вы знаете, что у Zend Log может быть несколько писателей 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;
  }

}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...