Je construis une application utilisant Zend Cadre. L'application nécessite une journalisation intensive pour chaque action ou chaque fonction dans le code.
Donc, mon code la plupart du temps ressemble à ceci: p> parfois je dois même me connecter 2 tables, la majeure partie du code de journalisation est donc doublée et les fonctions commencent à être compliquées et longues. P> J'utilise Certaines personnes ont suggéré une programmation orientée d'aspect (AOP), mais malheureusement, un AOP pour PHP n'est pas acceptable pour mon client Donc, je cherche une solution orientée objet ou une meilleure pratique. P> Remarque: P> Juste pour préciser mon problème n'est pas comment utiliser zend_log code> pour la journalisation afin que mon problème ne soit pas la classe de journalisation. elle-même, mais c'est comment séparer le code de journalisation de la fonctionnalité de code elle-même et maintenir la séparation des préoccupations. P>
zend_log code>, mais comment ajouter de la journalisation à mon code d'application en général. p> p>
4 Réponses :
Vous savez que Zend Log peut avoir plus d'un écrivain Son simple comme création d'une fonction unique de classe < / p> Bien sûr, vous pouvez modifier cet échantillon pour être plus rapide avec de nombreuses façons, mais il devrait expliquer l'idée p> p>
En fait, mon problème ne consiste pas à utiliser zend_log code> classe. Il s'agit de garder mon code propre et de maintenir la séparation des préoccupations dans les classes d'occasion.
+1 pour me rappeler que je pouvais attacher plus d'un écrivain au journal. :)
J'ai remarqué que vous avez insensé l'enregistreur dans une fonction. J'ai essayé de le faire à la portée de la classe et j'ai échoué. Est-ce normal? Est-ce que j'instende à chaque fonction un nouveau enregistreur + écrivain?
Si vous ne voulez pas utiliser d'outils externes, vous pouvez écrire une sorte de wrapper d'observateur autour de debug_backtrace qui boucle via la backtrage et compare tous les appels de fonction à une carte de matrice fournie par l'enveloppe et si c'est un coup, écrit le message de journal respectif avec votre texte de message personnalisé. Ce serait une séparation complète du code où vous auriez juste besoin d'exécuter cette classe d'observateurs à la fin de chaque script.
Comme pour un exemple, je pense que tout ce dont vous avez besoin est dans les exemples du manuel PHP. Néanmoins, voici un pseudo-code pour éclairer ce que je veux dire: p> Lamap doit déjà contenir le message que vous souhaitez enregistrer pour chaque événement. P> Si cela ne vous dérange pas d'utiliser un outil externe (que je pense, c'est la meilleure option), vous pouvez travailler avec xdebug et Webgrind ou similaire. P> BTW: Vous pouvez être intéressé par Monitorix < / a>, qui est une extension de Zend_Log avec beaucoup de jolis à enregistrer automatiquement à une table de base de données (comme la journalisation de la requête lente, l'erreur PHP et la journalisation d'exception, la journalisation d'erreur JavaScript). P> P>
Pourriez-vous s'il vous plaît expliquer plus sur la première partie de votre réponse ?. Souhaitez-vous publier des exemples de code sur l'utilisation du débog_bactrace code>, s'il vous plaît?
Parfois, je dois même vous connecter à 2 tables, la majeure partie du code de journalisation est donc doublée et que les fonctions commencent à être compliquées et longues. P>
Ce sera long. Si votre code fait beaucoup de journalisation, il sera em> em> sera long, car il sera à enregistrer, et chaque action de ligne qu'elle enregistre, signifie qu'il y a une ligne dans votre code. Cependant, cela ne devrait pas être compliqué dans tous les cas car la journalisation est l'une des choses les plus simples que vous puissiez faire. Ce qui m'inquiète, c'est que vous mentionnez «Parfois, je dois même vous connecter à 2 tables». Dans mon livre, une, deux, cinq, soixante ou mille tables est effectuée par une ligne em>. Le code n'est pas doublé pour chaque enregistreur. Si vous copiez une ligne et changeez
$ journal code> à
$ log2 code>, vous le faites clairement mal (TM). P>
Certaines personnes ont suggéré une programmation orientée forme (AOP), mais malheureusement, un AOP pour PHP n'est pas acceptable pour mon costumateur, je recherche donc une solution orientée objet ou une meilleure pratique. P> BlockQuote>
C'est bien, AOP. Cela a cependant des effectifs; Comme pour l'approche debug_backTrace, il y a une forte performance frappée. Cela, plus le code devient de plus en plus "magique" en ce sens que cela ne constitue pas clair lorsque vous regardez le code lui-même. Cela augmente le temps que vous avez débogué votre application. P>
My $ 0.02? Tout d'abord, ne vous répétez pas: une entrée de journal par action devrait suffire. Utilisez des enregistreurs flexibles pouvant être attachés à certaines classes au moment de l'exécution. Décidez si réellement em> enregistre le message dans l'enregistreur, en fonction de la "gravité" ou du "type". Dans l'ensemble, implémentez simplement le modèle d'observateur: P>
<?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( );
+1 pour le code de travail et une grande explication. Il suffit de vous demander cependant, pourquoi avez-vous créé la classe abstraite implémenter l'interface dans Abstract Class Logger implémente des observes code>?
@Songo surtout parce que j'ai déjà écrit l'interface, j'ai trouvé que les deux comportements courants qui pourraient aussi facilement être résumés à la classe mère. Mais si vous écrivez un observateur qui ne vous connecte pas, vous pourriez trouver l'interface utile de toute façon.
J'ai connecté mon service Zend2 à l'aide de mon Go! AOP PHP bibliothèque. Il est assez rapide et me permet de déboguer le code source d'origine avec Xdebug en mode développement. Cependant, c'est seulement bêta, sachez!
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; } }
Que pensez-vous que votre code devrait ressembler? Y a-t-il un peu de code souhaitable?
@akond Eh bien, je pensais à "attacher" les fonctions de journal à certaines fonctions dans les modèles ou les contrôleurs. Quelque chose comme l'enregistrement des fonctions à exécuter à certains points du code. Si cela est même applicable dans PHP bien sûr.
Bonne question et je pense que zf2 gérera ce genre de problème beaucoup plus élégamment avec le EventManager .
EventManager pourrait être une solution, mais je pense personnellement que l'injection de dépendance est plus appropriée.
Je suis curieux de savoir pourquoi l'AOP n'est pas acceptable pour le client; Cela vous dérangeriez-vous d'élaborer?
@mgroves En fait, mon client a déclaré que pour pouvoir prendre en charge la configuration PHP Certian, vous devrez être effectué sur le serveur. Il a également parlé de son effet sur la performance. Je ne suis pas un expert sur l'AOP et je n'ai entendu parler que de cela comme une suggestion à mon problème, mais je ne pouvais pas l'amener à s'accorder dessus.
@Richard Ayotte Dommage que ZF2 soit encore un long chemin à arriver :( J'ai vraiment pu faire usage de l'événementManager.