Skip to end of metadata
Go to start of metadata

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

Zend Framework: Zend_Log Component Proposal

Proposed Component Name Zend_Log
Developer Notes http://framework.zend.com/wiki/display/ZFDEV/Zend_Log
Proposers Mike Naberezny, with direction from Bill Karwin and Andi Gutmans
Revision 1.1 - 17 Jan 2007: Use "writer" instead of "handler" (Christopher Thompson) (wiki revision: 24)

Table of Contents

1. Overview

This proposal covers a complete rewrite of Zend_Log. It addresses concerns
raised in another proposal, "Zend_Log Dynamic", and also proposes new
features requested both by the Zend team and community members.

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • Usage must remain simple, on the same level as the current implementation.
    While the log4x loggers are a popular and powerful standard, Zend has
    decided to use their influence but not make a logger that is based on
    them. Instead, the logger will be closer in usage to PEAR's Log package.
  • The new logger will make little or no use of static functions.
    It must be easy to store logger instances for use with other components.
  • It should be possible and relatively simple to add new log writers. These
    should include additional ones from the community to be included with the
    package and also any specialized writers that end users might need to
    write for their own needs.
  • The tasks of managing data to be logged (loggers), the log backend
    (writers), and filtering log messages (filters) should be cleanly
    separated into different objects. It should be simple for users to register
    their own objects to perform these functions. However, the logger should
    have a simple default configuration and users should not need to be aware
    of these features until they are needed.

4. Dependencies on Other Framework Components

The dependencies will be determined primarily by which log writers are
developed. Possibly these will include:

  • Zend_Db
  • Zend_Cache
  • Zend_Mail
  • Zend_Http
  • Zend_Exception

5. Theory of Operation

Logger

The base logging class where writers and filters can be registered.

Users will able to add new levels with a method such as
$log->addLevel($name, $level) provided the name or level does not conflict
with an existing one. The default levels will always be present and can
never be overridden (without subclassing Zend_Log).

Message can be logged with a method such as $log->log($message, $level).
It will also provide convenience methods for each level, such as
$log->warn('message');

Log Levels

Each log level will have a name and a value. The default log levels
will mimic those from syslog:

0 Emergency: system is unusable
1 Alert: action must be taken immediately
2 Critical: critical conditions
3 Error: error conditions
4 Warning: warning conditions
5 Notice: normal but significant condition
6 Informational: informational messages
7 Debug: debug-level messages

User error levels may be any number above DEBUG (7).

Filter

Loggers will have the ability to filter by level. For more sophisticated
filtering, filter objects built by users can be registered and chained.

Writer

Initially, the following log writers will be developed:

  • Stream Writer (STDOUT/STDERR)
  • RDBMS Writer (Zend_Db)
  • File Writer
  • XML File Writer
  • Test Writer (mock for testing)

In the future, additional writers could be developed by the
community. Suggestions for these are:

  • Rotating File Writer (Rotate Logs)
  • Socket Writer (Log to TCP/IP)
  • Syslog Writer (UNIX/Linux Syslog)
  • NT Event Writer (Windows NT-based systems)
  • Email Writer (Zend_Mail)
  • HTTP Writer (Zend_Http_Client)

6. Milestones / Tasks

  • Iteration 1 - Base Logger implementation
  • Iteration 2 - Writer and Filter implementation
  • Iteration 3 - Log writer implementation (Test, Stream, File)
  • Iteration 4 - Additional writers (RDBMS, XML)
  • Iteration 5 - Documentation (DocBook)

Unit tests will be written as an integral and ongoing part of the
development process so no separate iteration is needed for them.

7. Class Index

  • Logger (Zend_Log)
  • Writer (Zend_Log_Writer_*)
  • Filter (Zend_Log_Filter_*)
  • Error (Zend_Log_*Exception)

8. Use Cases

UC-01 Simple Usage
UC-02 Using Multiple Log Writers
UC-03 Custom Log Levels
UC-04 Filtering for All Writers

A filter can be added to either Zend_Log, where it will be applied
to all writers. Either a simple level or a filter object may be passed.

UC-05 Filtering for a Specific Writer
UC-06 Use with the Registry

The current Zend_Log implementation uses all static functions. This new
implementation has no static functions. However, for users that prefer the
old usage, similar usage can be achieved by storing a logger object in the
registry.

9. Class Skeletons

Class skeletons will not be proposed at this stage. When development begins,
behavioral requirements of the objects will be expressed as tests and the
classes will then be developed based on these requirements using TDD.

]]></ac:plain-text-body></ac:macro>

]]></ac:plain-text-body></ac:macro>

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Jan 22, 2007

    <p>I noticed from the use cases that the names of convenience methods appear to be indeterministic. For example, a log message of <code>Zend_Log::CRITICAL</code> would correspond to an invocation of <code>Zend_Log::crit()</code>. Does this matter? User-defined log levels, however, appear to result in method names matching the level name (e.g., "foo").</p>

    <p>I'm not sure I fully understand the first sentence of UC-04; perhaps a slight rephrasing would help?</p>

    <p>How would I create a logger that writes messages only of a user-defined log level? For instance, let's assume that I want to write only "specialError" level messages both to a log file and to an e-mail address. Would this use case be considered in the scope of the proposal?</p>

    <p>It might be reasonable to use Zend_Log for application security auditing. For example, it is often valuable to collect information about certain security violations, such as logging when users attempt to perform unauthorized operations. I could imagine providing via the Zend_Auth and Zend_Acl APIs methods to plug in Zend_Log objects for providing for an audit trail.</p>

    <p>The proposal is shaping up nicely, and the approach of using use cases and test-driven development seems very appropriate to me.</p>

    1. Jan 24, 2007

      <blockquote><p>How would I create a logger that writes messages only of a user-defined log level?</p></blockquote>

      <p>I believe this was it:</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      // add a new level
      $log->addLevel('foo', 8);

      // log
      $log->foo('foo level message');
      ]]></ac:plain-text-body></ac:macro>

      <p>I would caution against using arbitrarily-abbreviated log level method names like "crit()".</p>

  2. Jan 24, 2007

    <p>I'd like to see two additional things: (1) The constructor should take a single argument, a log adapter; this would be more convenient, and ensure no logging happens prior to logging a message. (2) I like the logLevel(), but want to make sure that the convenience methods would not change the global log level (I assume they wouldn't, but feel this should be explicit criteria).</p>

  3. Jan 24, 2007

    <ac:macro ac:name="note"><ac:parameter ac:name="title">Official Zend comment</ac:parameter><ac:rich-text-body>
    <p>We have reviewed this proposal and we approve it for inclusion in the Zend Framework.</p>

    <p>This represents a redesign of the existing Zend_Log class, and it is the intention to remove the old implementation in favor of this new design.</p>

    <p>We have a few specific comments that we would like to see addressed in the design:</p>
    <ul>
    <li>We anticipate that in most cases, only one log writer would be needed. Can a single writer object be specified in the Zend_Log() constructor? This would be equivalent to adding the first writer after instantiating the Zend_Log object, but it would just make the code one line shorter.</li>
    <li>It seems to make sense that there should be at least one writer, or else any messages written will go nowhere. With the current design it is possible to send messages to a Zend_Log object before adding any writers to it. What happens in this case? Is it reasonable to require at least one writer be registered, and if none are registered, the logging methods throw an exception? Or is there a legitimate use case for writing a message while having no writers registered?</li>
    <li>There should be either an interface or an abstract class defined for filters, so that users know how to write their own. Also, we had wished we could see what a custom filter implementation would look like, but we understand why the class skeletons are omitted from this proposal.</li>
    <li>We understand that it's traditional log4j-like functionality for filtering to restrict output to a given log level or lower level. How would one restrict output to one specific log level (other than level 0)? Can this be done with a custom filter object?</li>
    </ul>
    </ac:rich-text-body></ac:macro>

  4. Feb 03, 2007

    <p>Hi. I have a feature request. I have actually stumbled upon it while replying to Gavin's comment concerning my own proposal:
    <a class="external-link" href="http://framework.zend.com/wiki/display/ZFPROP/Zend_Controller_Router_RegexRoute+-+Michael+Minicki">http://framework.zend.com/wiki/display/ZFPROP/Zend_Controller_Router_RegexRoute+-+Michael+Minicki</a></p>

    <p>In RewriteRouter I need to log a couple of messages or a pack of data at once:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    if ($this->_log) {
    $message = 'Type: ' . get_class($route) . '; ' .
    'Route: ' . $route->getMatchString() . '; ' .
    'Path: ' . $path . '; ' .
    'Result: ' . $result;
    $this->_log->debug($message);
    }
    ]]></ac:plain-text-body></ac:macro>

    <p>It gave me an idea to pass the message as an array:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    $message = array(
    'Type' => get_class($route),
    'Route' => $route->getMatchString(),
    'Path' => $path,
    'Result' => $result
    );
    $this->_log->debug($message);
    ]]></ac:plain-text-body></ac:macro>

    <p>Which can then be rendered differently by different writers. </p>

    <p>For example, XML writer can render it as:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    <LogItem>
    <Type>Zend_Controller_Router_Route</Type>
    <Route>:action/:year</Route>
    <Path>users/martel</Path>
    <Result>false</result>
    </LogItem>
    ]]></ac:plain-text-body></ac:macro>

    <p>While file writer could use multiline entry:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    2006-02-03 16:54 Debug:
    \t Type: Zend_Controller_Router_Route
    \t Route: :action/:year
    \t Path: users/martel
    \t Result: false
    ]]></ac:plain-text-body></ac:macro>

    <p>And DB of course can keep it on one line separated with semicolon.</p>

    <p>Hope you will like it too <ac:emoticon ac:name="wink" /></p>

  5. Mar 12, 2007

    <p>How about setting log format?</p>

    <p>for example:<br />
    $log->setFormat("%datetime <ac:link><ri:page ri:content-title="%pid" /></ac:link> %level %message");</p>

    <p>by doing:<br />
    $log->warning("Live support not ready");</p>

    <p>should produce:<br />
    2007-03-12 14:11:38 <ac:link><ri:page ri:content-title="8439" /></ac:link> WARNING Live support not ready</p>

    <p>There should be predefined keyword for formatting, <br />
    the most important IMHO:</p>

    <p>datetime, current locale date time<br />
    date,<br />
    time,<br />
    pid, current process ID<br />
    level, current log level<br />
    message, log message</p>

    <p>Thanks</p>

  6. Aug 12, 2007

    <p>Is there an existing proposal to contribute a rotating log file writer?</p>

    <p>If yes, could someone kindly point me to it?</p>

    1. Aug 12, 2007

      <p>We have had two feature requests logged in our issue tracker regarding log rotation solutions.
      <a class="external-link" href="http://framework.zend.com/issues/browse/ZF-263">http://framework.zend.com/issues/browse/ZF-263</a>
      <a class="external-link" href="http://framework.zend.com/issues/browse/ZF-563">http://framework.zend.com/issues/browse/ZF-563</a></p>

      <p>I continue to believe that this is not appropriate to solve in a PHP class. Linux has the 'logrotate' command for this purpose.
      <a class="external-link" href="http://www.linuxcommand.org/man_pages/logrotate8.html">http://www.linuxcommand.org/man_pages/logrotate8.html</a></p>

      <p>Windows does not have a standard tool to do this. It needs to be done as part of restarting the HTTP server, so it cannot be a PHP solution.</p>

      1. Aug 13, 2007

        <p>Thanks for the prompt response, Bill.</p>

        <p>First time I heard of <em>logrotate.</em> I definitely prefer that option since I'm on Linux. I agree too that it's best not to duplicate functionality if there's already a good tool that provides it.</p>

        <p>P.S. I enjoyed your recent ZF Overview webinar. Even as I'm quite familiar with the content you delivered, I was all the same enwrapped by your delivery. Nice work with ZF, all-round. </p>