Преглед изворни кода

ZF-8491: create Zend Monitor log writer and bootstrap resource

git-svn-id: http://framework.zend.com/svn/framework/standard/trunk@19493 44c647ce-9c0f-0410-b52a-842ac1e357ba
matthew пре 16 година
родитељ
комит
0498034097

+ 47 - 0
documentation/manual/en/module_specs/Zend_Application-AvailableResources-ZendMonitor.xml

@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect2 id="zend.application.available-resources.zendmonitor">
+    <title>Zend_Application_Resource_Zendmonitor</title>
+
+    <para>
+        <classname>Zend_Application_Resource_Zendmonitor</classname> is a resource that instantiates
+        a <classname>Zend_Log</classname> instance with an attached <link
+            linkend="zend.log.writers.zendmonitor">Zend_Log_Writer_ZendMonitor</link> writer. It can
+        then be used within your application to log events to Zend Server's event monitor.
+    </para>
+
+    <para>
+        It has no configuration parameters; as such, you will simply need to specify it within your
+        configuration. Examples include:
+    </para>
+
+    <programlisting language="dosini"><![CDATA[
+; INI configuration:
+resources.zendmonitor[] =
+]]></programlisting>
+
+    <programlisting language="xml"><![CDATA[
+<!-- XML configuration -->
+<resources>
+    <zendmonitor />
+</resources>
+]]></programlisting>
+
+    <para>
+        Within your application, you can retrieve the logger from your bootstrap object:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$bootstrap->bootstrap('Zendmonitor');
+$log = $bootstrap->getResource('Zendmonitor');
+$log->info('some message');
+]]></programlisting>
+
+    <para>
+        The default project structure generated by the <command>zf.sh</command> or
+        <command>zf.bat</command> commands will configure the resource by default, and adds code
+        into the <classname>ErrorController</classname> to log application exceptions to the Zend
+        Server event monitor. You can disable this functionality simply by removing the resource
+        configuration from your configuration file.
+    </para>
+</sect2>

+ 1 - 0
documentation/manual/en/module_specs/Zend_Application-AvailableResources.xml

@@ -16,4 +16,5 @@
     <xi:include href="Zend_Application-AvailableResources-Router.xml" />
     <xi:include href="Zend_Application-AvailableResources-Session.xml" />
     <xi:include href="Zend_Application-AvailableResources-View.xml" />
+    <xi:include href="Zend_Application-AvailableResources-ZendMonitor.xml" />
 </sect1>

+ 124 - 0
documentation/manual/en/module_specs/Zend_Log-Writers-ZendMonitor.xml

@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect2 id="zend.log.writers.zendmonitor">
+    <title>Writing to the Zend Server Monitor</title>
+
+    <para>
+        <classname>Zend_Log_Writer_ZendMonitor</classname> allows you to log events via Zend
+        Server's Monitor API. This allows you to aggregate log messages for your entire application
+        environment in a single location. Internally, it simply uses the
+        <functionname>monitor_custom_event()</functionname> function from the Zend Monitor API.
+    </para>
+
+    <para>
+        One particularly useful feature of the Monitor API is that it allows you to specify
+        arbitrary custom information alongside the log message. For instance, if you wish to log an
+        exception, you can log not just the exception message, but pass the entire exception object
+        to the function, and then inspect the object within the Zend Server event monitor.
+    </para>
+
+    <note>
+        <title>Zend Monitor must be installed and enabled</title>
+
+        <para>
+            In order to use this log writer, Zend Monitor must be both installed and enabled.
+            However, it is designed such that if Zend Monitor is not detected, it will simply act as
+            a null logger.
+        </para>
+    </note>
+
+    <para>
+        Instantiating the <classname>ZendMonitor</classname> log writer is trivial:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$writer = new Zend_Log_Writer_ZendMonitor();
+$log    = new Zend_Log($writer);
+]]></programlisting>
+
+    <para>
+        Then, simply log messages as usual:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$log->info('This is a message');
+]]></programlisting>
+
+    <para>
+        If you want to specify additional information to log with the event, pass that information
+        in a second parameter:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$log->info('Exception occurred', $e);
+]]></programlisting>
+
+    <para>
+        The second parameter may be a scalar, object, or array; if you need to pass multiple pieces
+        of information, the best way to do so is to pass an associative array.
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$log->info('Exception occurred', array(
+    'request'   => $request,
+    'exception' => $e,
+));
+]]></programlisting>
+
+    <para>
+        Within Zend Server, your event is logged as a "custom event". From the "Monitor" tab, select
+        the "Events" sub-item, and then filter on "Custom" to see custom events.
+    </para>
+
+    <mediaobject>
+        <imageobject>
+            <imagedata fileref="figures/zend.log.writers.zendmonitor-events.png"
+                format="PNG"></imagedata>
+
+            <caption>
+                <para>
+                    Events in Zend Server's Monitor dashboard
+                </para>
+            </caption>
+        </imageobject>
+    </mediaobject>
+
+    <para>
+        In this screenshot, the first two events listed are custom events logged via the
+        <classname>ZendMonitor</classname> log writer. You may then click on an event to view all
+        information related to it.
+    </para>
+
+    <mediaobject>
+        <imageobject>
+            <imagedata fileref="figures/zend.log.writers.zendmonitor-event.png"
+                format="PNG"></imagedata>
+
+            <caption>
+                <para>
+                    Event detail in Zend Server's Monitor
+                </para>
+            </caption>
+        </imageobject>
+    </mediaobject>
+
+    <para>
+        Clicking on the "Custom" sub tab will detail any extra information you logged by passing the
+        second argument to the logging method. This information will be logged as the
+        <varname>info</varname> subkey; you can see that the request object was logged in this
+        example.
+    </para>
+
+    <note>
+        <title>Integration with Zend_Application</title>
+
+        <para>
+            The <classname>ZendMonitor</classname> log writer has a corresponding <link
+                linkend="zend.application.available-resources.zendmonitor"><classname>Zend_Application</classname>
+                bootstrap resource</link>. If you create your
+            project using the <command>zf.sh</command> or <command>zf.bat</command> command, this
+            resource will be registered for you by default, and used in your
+            <classname>ErrorController</classname> to log application exceptions.
+        </para>
+    </note>
+</sect2>

+ 1 - 0
documentation/manual/en/module_specs/Zend_Log-Writers.xml

@@ -98,6 +98,7 @@ $logger->info('Informational message');
   <xi:include href="Zend_Log-Writers-Firebug.xml" />
   <xi:include href="Zend_Log-Writers-Mail.xml" />
   <xi:include href="Zend_Log-Writers-Syslog.xml" />
+  <xi:include href="Zend_Log-Writers-ZendMonitor.xml" />
 
   <sect2 id="zend.log.writers.null">
     <title>Stubbing Out the Writer</title>

+ 74 - 0
library/Zend/Application/Resource/Zendmonitor.php

@@ -0,0 +1,74 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Application
+ * @subpackage Resource
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @category   Zend
+ * @package    Zend_Application
+ * @subpackage Resource
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+class Zend_Application_Resource_Zendmonitor extends Zend_Application_Resource_ResourceAbstract
+{
+    /**
+     * @var Zend_Log
+     */
+    protected $_log;
+
+    /**
+     * Initialize log resource
+     * 
+     * @return Zend_Log
+     */
+    public function init()
+    {
+        return $this->getLog();
+    }
+
+    /**
+     * Get log instance
+     *
+     * Lazy-loads instance if not registered
+     * 
+     * @return Zend_Log
+     */
+    public function getLog()
+    {
+        if (null === $this->_log) {
+            $this->setLog(new Zend_Log(new Zend_Log_Writer_ZendMonitor()));
+        }
+        return $this->_log;
+    }
+
+    /**
+     * Set log instance
+     * 
+     * @param  Zend_Log $log 
+     * @return Zend_Application_Resource_Zendmonitor
+     */
+    public function setLog(Zend_Log $log)
+    {
+        $this->_log = $log;
+        return $this;
+    }
+}

+ 36 - 2
library/Zend/Log.php

@@ -100,7 +100,21 @@ class Zend_Log
     {
         $priority = strtoupper($method);
         if (($priority = array_search($priority, $this->_priorities)) !== false) {
-            $this->log(array_shift($params), $priority);
+            switch (count($params)) {
+                case 0:
+                    /** @see Zend_Log_Exception */
+                    require_once 'Zend/Log/Exception.php';
+                    throw new Zend_Log_Exception('Missing log message');
+                case 1:
+                    $message = array_shift($params);
+                    $extras = null;
+                    break;
+                default:
+                    $message = array_shift($params);
+                    $extras  = array_shift($params);
+                    break;
+            }
+            $this->log($message, $priority, $extras);
         } else {
             /** @see Zend_Log_Exception */
             require_once 'Zend/Log/Exception.php';
@@ -113,10 +127,11 @@ class Zend_Log
      *
      * @param  string   $message   Message to log
      * @param  integer  $priority  Priority of message
+     * @param  mixed    $extras    Extra information to log in event
      * @return void
      * @throws Zend_Log_Exception
      */
-    public function log($message, $priority)
+    public function log($message, $priority, $extras = null)
     {
         // sanity checks
         if (empty($this->_writers)) {
@@ -138,6 +153,25 @@ class Zend_Log
                                     'priorityName' => $this->_priorities[$priority]),
                               $this->_extras);
 
+        // Check to see if any extra information was passed
+        if (!empty($extras)) {
+            $info = array();
+            if (is_array($extras)) {
+                foreach ($extras as $key => $value) {
+                    if (is_string($key)) {
+                        $event[$key] = $value;
+                    } else {
+                        $info[] = $value;
+                    }
+                }
+            } else {
+                $info = $extras;
+            }
+            if (!empty($info)) {
+                $event['info'] = $info;
+            }
+        }
+
         // abort if rejected by the global filters
         foreach ($this->_filters as $filter) {
             if (! $filter->accept($event)) {

+ 99 - 0
library/Zend/Log/Writer/ZendMonitor.php

@@ -0,0 +1,99 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Log
+ * @subpackage Writer
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** Zend_Log_Writer_Abstract */
+require_once 'Zend/Log/Writer/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Log
+ * @subpackage Writer
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+class Zend_Log_Writer_ZendMonitor extends Zend_Log_Writer_Abstract
+{
+    /**
+     * Is Zend Monitor enabled?
+     * @var bool
+     */
+    protected $_isEnabled = true;
+
+    /**
+     * @throws Zend_Log_Exception if Zend Monitor extension not present
+     */
+    public function __construct()
+    {
+        if (!function_exists('monitor_custom_event')) {
+            $this->_isEnabled = false;
+        }
+    }
+
+    /**
+     * Is logging to this writer enabled?
+     *
+     * If the Zend Monitor extension is not enabled, this log writer will 
+     * fail silently. You can query this method to determine if the log 
+     * writer is enabled.
+     * 
+     * @return bool
+     */
+    public function isEnabled()
+    {
+        return $this->_isEnabled;
+    }
+
+    /**
+     * Log a message to this writer.
+     *
+     * @param  array $event  log data event
+     * @return void
+     */
+    public function write($event)
+    {
+        if (!$this->isEnabled()) {
+            return;
+        }
+
+        parent::write($event);
+    }
+
+    /**
+     * Write a message to the log.
+     *
+     * @param  array  $event  log data event
+     * @return void
+     */
+    protected function _write($event)
+    {
+        $priority = $event['priority'];
+        $message  = $event['message'];
+        unset($event['priority'], $event['message']);
+
+        if (!empty($event)) {
+            monitor_custom_event($priority, $message, $event);
+        } else {
+            monitor_custom_event($priority, $message);
+        }
+    }
+}

+ 2 - 0
tests/Zend/Application/Resource/AllTests.php

@@ -39,6 +39,7 @@ require_once 'Zend/Application/Resource/ModulesTest.php';
 require_once 'Zend/Application/Resource/NavigationTest.php';
 require_once 'Zend/Application/Resource/SessionTest.php';
 require_once 'Zend/Application/Resource/ViewTest.php';
+require_once 'Zend/Application/Resource/ZendmonitorTest.php';
 
 /**
  * @category   Zend
@@ -70,6 +71,7 @@ class Zend_Application_Resource_AllTests
         $suite->addTestSuite('Zend_Application_Resource_NavigationTest');
         $suite->addTestSuite('Zend_Application_Resource_SessionTest');
         $suite->addTestSuite('Zend_Application_Resource_ViewTest');
+        $suite->addTestSuite('Zend_Application_Resource_MonitorTest');
 
         return $suite;
     }

+ 83 - 0
tests/Zend/Application/Resource/ZendmonitorTest.php

@@ -0,0 +1,83 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Application
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+require_once dirname(__FILE__) . '/../../../TestHelper.php';
+
+/** Zend_Application_Resource_Resource */
+require_once 'Zend/Application/Resource/Resource.php';
+
+/** Zend_Application_Resource_ResourceAbstract */
+require_once 'Zend/Application/Resource/ResourceAbstract.php';
+
+/** Zend_Application_Resource_Zendmonitor */
+require_once 'Zend/Application/Resource/Zendmonitor.php';
+
+/** Zend_Log */
+require_once 'Zend/Log.php';
+
+/** Zend_Log_Writer_ZendMonitor */
+require_once 'Zend/Log/Writer/ZendMonitor.php';
+
+/** Zend_Log_Writer_Mock */
+require_once 'Zend/Log/Writer/Mock.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Application
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Zend_Application_Resource
+ */
+class Zend_Application_Resource_ZendmonitorTest extends PHPUnit_Framework_TestCase
+{
+    public function setUp()
+    {
+        $this->resource = new Zend_Application_Resource_Zendmonitor();
+    }
+
+    public function testGetLogLazyLoadsLog()
+    {
+        $log = $this->resource->getLog();
+        $this->assertTrue($log instanceof Zend_Log);
+    }
+
+    public function testInitReturnsLogInstance()
+    {
+        $log = $this->resource->init();
+        $this->assertTrue($log instanceof Zend_Log);
+    }
+
+    public function testInitReturnsSameLogInstanceAsGetter()
+    {
+        $log = $this->resource->getLog();
+        $this->assertSame($log, $this->resource->init());
+    }
+
+    public function testSetterWillOverwriteExistingLogInstance()
+    {
+        $existing = $this->resource->getLog();
+        $this->resource->setLog($log = new Zend_Log(new Zend_Log_Writer_Mock()));
+        $this->assertNotSame($existing, $this->resource->getLog());
+        $this->assertSame($log, $this->resource->getLog());
+    }
+}

+ 42 - 2
tests/Zend/Log/LogTest.php

@@ -20,8 +20,7 @@
  * @version    $Id$
  */
 
-/** PHPUnit_Framework_TestCase */
-require_once 'PHPUnit/Framework/TestCase.php';
+require_once dirname(__FILE__) . '/../../TestHelper.php';
 
 /** Zend_Log */
 require_once 'Zend/Log.php';
@@ -29,6 +28,9 @@ require_once 'Zend/Log.php';
 /** Zend_Log_Writer_Mock */
 require_once 'Zend/Log/Writer/Mock.php';
 
+/** Zend_Log_Writer_Stream */
+require_once 'Zend/Log/Writer/Stream.php';
+
 /**
  * @category   Zend
  * @package    Zend_Log
@@ -201,4 +203,42 @@ class Zend_Log_LogTest extends PHPUnit_Framework_TestCase
         $this->assertTrue(array_key_exists($field, $event));
         $this->assertEquals($value, $event[$field]);
     }
+
+    /**
+     * @group ZF-8491
+     */
+    public function testLogAcceptsExtrasParameterAsArrayAndPushesIntoEvent()
+    {
+        $logger = new Zend_Log($mock = new Zend_Log_Writer_Mock);
+        $logger->info('foo', array('content' => 'nonesuch'));
+        $event = array_shift($mock->events);
+        $this->assertContains('content', array_keys($event));
+        $this->assertEquals('nonesuch', $event['content']);
+    }
+
+    /**
+     * @group ZF-8491
+     */
+    public function testLogNumericKeysInExtrasArrayArePassedToInfoKeyOfEvent()
+    {
+        $logger = new Zend_Log($mock = new Zend_Log_Writer_Mock);
+        $logger->info('foo', array('content' => 'nonesuch', 'bar'));
+        $event = array_shift($mock->events);
+        $this->assertContains('content', array_keys($event));
+        $this->assertContains('info', array_keys($event));
+        $this->assertContains('bar', $event['info']);
+    }
+
+    /**
+     * @group ZF-8491
+     */
+    public function testLogAcceptsExtrasParameterAsScalarAndAddsAsInfoKeyToEvent()
+    {
+        $logger = new Zend_Log($mock = new Zend_Log_Writer_Mock);
+        $logger->info('foo', 'nonesuch');
+        $event = array_shift($mock->events);
+        $this->assertContains('info', array_keys($event));
+        $info = $event['info'];
+        $this->assertContains('nonesuch', $info);
+    }
 }