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_Writer_Mail Component Proposal

Proposed Component Name Zend_Log_Writer_Mail
Developer Notes http://framework.zend.com/wiki/display/ZFDEV/Zend_Log_Writer_Mail
Proposers Brian DeShong
Matthew Weier O'Phinney (Zend Liaison)
Revision 1.0 - 26 January 2008 - Initial creation (wiki revision: 25)

Table of Contents

1. Overview

Zend_Log_Writer_Mail is a Zend_Log writer for sending log entries to recipient(s) via email.

Proposal of this class is motivated by use in a batch script environment, where logs need to be kept, but developers also need to be notified via email of any notices, warnings, errors, etc.; will be illustrated below in "Use Cases."

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • This component will attempt to deliver appropriate log entries to given recipient(s)
  • This component will adhere to standard Zend_Log_Writer formatting and filtering conventions
  • This component will allow for a minimum of a plaintext message body, with an optional HTML message body
  • This component will dynamically set mail subject to include the number of entries that occurred per-priority level if the caller so chooses
  • This component will not open connections to the Zend_Mail transport until a message is ready to be sent
  • This component will not allow for on-demand mailing of log entries

4. Dependencies on Other Framework Components

  • Zend_Mail
  • Zend_Log_Writer_Abstract
  • Zend_Log_Exception
  • Zend_Layout

5. Theory of Operation

User instantiates a Zend_Mail object and populates it with data for recipients that should be notified of any log entries. Zend_Mail object is then passed to constructor for Zend_Log_Writer_Mail object.

Optionally, user may instantiate an instance of Zend_Layout to provide an HTML-based message body.

Zend_Log_Writer_Mail::_write() builds up an array of log entry lines to use as the body of the email message to the recipients.

Email should not be sent upon the call of Zend_Log_Writer_Mail::_write(); the email should be sent upon the call to shutdown() if there are log entry lines to use in the body.

Once email has been sent to recipients, reference to Zend_Mail object is NOT removed as the user may want to continue using it.

6. Milestones / Tasks

  • Milestone 1: [DONE] Initial class code is drafted for proposal
  • Milestone 2: [DONE] Proposal finalized and readied for review
  • Milestone 3: Working prototype checked in to incubator for community review
  • Milestone 3: Unit tests are created with 90%+ code coverage (assume that we can't test the actual receiving of email?)
  • Milestone 4: Initial documentation completed

7. Class Index

  • Zend_Log_Writer_Mail

8. Use Cases

UC-01
UC-02
UC-03
UC-04
UC-05

9. Class Skeletons

]]></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. Mar 05, 2008

    <p>Whats happening in a case when you got more than one message of one type. Do you get a Mail per message, or do you collect messages of one type and sending all messages in one mail?</p>

    1. Mar 05, 2008

      <p>You only get one email with all of the log messages in it, regardless of what their log level is.</p>

      <p>Note how write() builds up an array of the log entries, while shutdown() actually sends the email to the recipient(s).</p>

      <p>Does that make it a bit more clear?</p>

      1. Mar 21, 2008

        <p>HI</p>

        <p>Yes thanks. Thats good. I think this log writer is a good enhancement.<br />
        But, as Lars said already - Sometimes this behavior is wanted but generaly it can be a great danger to send logs via mail on every request.<br />
        Perhaps it would be better to have two options for this writer. First Option ,lets say a Collector, which collects the logs in a serialized form in a file and sends it on a defined time or in an defined interval.<br />
        And the other option right this way you proposed it already.</p>

        <p>Tom</p>

  2. Jun 23, 2008

    <p>But as far as I understand, you get one mail per request? Logging straightly per mail is considered a bad practice as it increases the danger of getting DOSsed by accident. If error XYZ (the one, where you send the mail) occurs it will likely occur more often and therefore the application might bring down the mail server too. So I'm not sure if it is a good idea to include such a component in general.</p>

    1. Mar 14, 2008

      <p>In this case, the developer could implement Zend_Log_Filter_Interface when determining if the log entry should be accepted. The mechanics of this would be something along the lines of storing recently-sent log entries in persistent storage (a temp file, for example) and checking if that entry has been sent within the past X minutes.</p>

      <p>My personal argument is that if, say, you're writing a file with a batch process, but have run out of disk space, the developer(s) should be notified so they can work to correct that problem ASAP. In an event such as this, receiving a regular email goes a long way to getting someone notified of the problem and having it dealt with in a timely manner.</p>

      <p>Point being: some cases need frequent sending of these emails, while you may want to be more forgiving with others. You leave this choice up to the developer by allowing them to use a filter.</p>

      <p>What do you think?</p>

      1. Mar 20, 2008

        <p>I agree that the developer should decide if it's ok to use this.</p>

        1. Jun 23, 2008

          <p>The developer decides for what systems are used? God thanks, not in my world <ac:emoticon ac:name="smile" /></p>

      2. Jun 23, 2008

        <p>I would like to see the load problem addressed. Monitoring for disks is pretty easy to implement, throughput control for an email system is much harder. I fear if we provider Zend_Log_Writer_Mail we encourage users to do that.<br />
        Filtering doesn't solve that problem. If a certain condition is met under which email logging is used it is likely to occur again and again. Log to the database, and send mails from a batch process.</p>

        1. Jun 24, 2008

          <p>I'm not sure that I understand this comment. By "load problem," are you referring to the potentially large amount of emails that could be sent during some sort of a critical failure?</p>

          <p>If so, then what solution would you propose that is A) portable, B) doesn't have any unreasonable dependencies, and C) is going to work well for the majority of users?</p>

          <p>Off the top of my head, I can dream up a solution along these lines:</p>

          <p>1) When building messages to be mailed, exclude or otherwise strip off any date/time values so you're just dealing with the raw error messages being logged/emailed; you want to examine the unique error messages, so stripping off the date/time ensures that they're not always unique strings.</p>

          <p>2) Take that string of messages and hash it somehow (MD5, etc.)</p>

          <p>3) Upon sending email, store the hash of the error messages to a flat file on disk along with the Unix timestamp of when those messages were last sent and the email recipient(s) that it was sent to.</p>

          <p>4) Before sending future emails, read from file of MD5s to see if current error messages to recipient(s) were emailed within the past X seconds or minutes (where X is able to be configured by the user). If error messages were emailed within that time period, don't email them again. Otherwise, send the mail normally.</p>

          <p>However, I don't necessarily feel that this should be enabled by default as most users may not want to use such functionality. Thus, they should have to enable it somehow and explicitly request that the class throttle messages in the manner outlined above or something similar.</p>

          <p>Regarding "log to a database," I definitely wouldn't want to introduce any database dependencies here as this seems like an inappropriate place.</p>

          <p>What do you think? Also, let me know if I totally misinterpreted your comment as I found it a bit unclear. Thanks!</p>

  3. Mar 20, 2008

    <p>+1</p>

  4. Mar 27, 2008

    <p>I'm using this implementation in a new project and it seems to work fine. I'm filtering the log messages to mail at a very low level (Zend_Log::CRIT). The rest is logged into a database.</p>

    <p>A nice other tric I did, was to initially set the level for the mail writer to Zend_Log::WARN, and when the database connection is succesful I alter the filter level to Zend_Log::CRIT (subclassed Zend_Log_Filter_Priority with a setPriority() function)</p>

    1. Mar 27, 2008

      <p>Hey Vincent,</p>

      <p>Wow, that's great news! You're the first I've heard of anyone (other than myself) actually using this. Feel free to post any additional questions or comments here. Maybe we can get the class into the incubator soon.</p>

      1. Apr 22, 2008

        <p>I'm using it without problems <ac:emoticon ac:name="laugh" /></p>

  5. May 25, 2008

    <p>How about a possibility to change the subject depending on the different levels of errors, warnings and notices? Having this running for cronjobs it might be interesting to make reports with more critical warnings seen better. Or is it better to implement two different writers with different priorities for this case?</p>

    <p>Also what about chaning the subject to display the number of log entries related to in this mail?</p>

    1. May 26, 2008

      <p>Hi Benjamin,</p>

      <p>Good feedback, thanks!</p>

      <p>Off the top of my head, it seems like the simplest way to implement something like this would be with, say, some pre-defined tokens in the subject line. For example, you could set your subjects to:</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      "Process Credit Cards: [numErrors] errors, [numWarnings] warnings"
      ]]></ac:plain-text-body></ac:macro>

      <p>...or even...</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      "Process Credit Cards ([errorSummary])"
      ]]></ac:plain-text-body></ac:macro>

      <p>...which would yield:</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      "Process Credit Cards (warn: 4, error: 3, info: 14)"
      ]]></ac:plain-text-body></ac:macro>

      <p>For the record, I'd use curly braces instead of brackets, but the curly braces don't seem to render well on the wiki page.</p>

      <p>Also, you suggest that you may want to change the subject for different levels of errors. I'd argue that this is unnecessary sheerly because, during the course of a script, it can generate many log messages at each level. For example, if a run generated, say, three (3) warnings and (4) errors, then what would the subject line say?</p>

      <p>That's why I suggested the tokens above, which would be replaced just before the message itself is sent.</p>

      <p>However, I would also argue that such token replacement could (and maybe should?) be done by the caller. As the proposer of this, I can't assume that all consumers of it will want to display the log message data in the same way. I can see how it might be useful, but perhaps its place is not in the Zend_Log_Writer_Mail class itself.</p>

      <p>What do you all think? If there's enough +1s for this, it'd be worth adding.</p>

      1. Jun 24, 2008

        <p>Hi,</p>

        <p>I think it is a very good matter to configurable the mail subject by mail content but I would set the percent characters instead of curly braces because it is used in Zend_Log_Formater.<br />
        In my opinion is it the best way to make the mail subject configurable in Zend_Log_Writer_Mail using the Zend_Log_Formater to format the subject because if I set the subject in the mail object given to the log writer the log writer can't change this:<br />
        'Zend_Mail_Exception' with message 'Subject set twice'</p>

        <p>1) Which formaters can be allowed to format a subject ?</p>

        <p>2) Is it a good idea to set a Zend_Mail object directly to the writer or is it better to set only a Zend_Mail_Transporter to it and implement configurable from/to/subject/header - part in Zend_Log_Writer_Mail or change the Zend_Mail class to implement overwritable mail subject ?</p>

    2. Nov 15, 2008

      <p>I've implemented something along these lines in the final proposal. Now the number of entries per-priority level can be optionally appended to the email subject by using the setSubjectPrependText() method.</p>

      <p>Note that I had to go this route because Zend_Mail::setSubject() will only allow the subject to be set once. As such, the client developer cannot call Zend_Mail::setSubject(), then have Zend_Log_Writer_Mail reset it to append on any entry counts.</p>

      <p>It seems like this is a nice middle ground.</p>

  6. Jun 23, 2008

    <p>Just a note that this is also being dealt with in the Issue Tracker at:</p>

    <p><a href="http://framework.zend.com/issues/browse/ZF-1322">http://framework.zend.com/issues/browse/ZF-1322</a></p>

    1. Sep 20, 2008

      <p>+</p>

      <p>The use of Zend_Layout (as shown there) could be a good idea...</p>

      1. Oct 24, 2008

        <p>Also a great point here; sorry it's taken me forever to dig deeper into the comments here.</p>

        <p>I'll look into integrating Zend_Layout soon and will update the proposal accordingly.</p>

  7. Sep 20, 2008

    <p>What about making the sending behaviour configurable? Imagine some kind of daemon or long-running script - logs would "never" be mailed, or at least surely too late.</p>

    <p>Here what changes could look like:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    protected $_deferDelivery = true;

    public function setDeferredDelivery($deferred = true)
    {
    if (! is_bool($deferred))

    Unknown macro: { throw new Zend_Log_Exception('Deferral has to be boolean'); }

    }

    protected function _write($event)
    {
    if ($this->_deferDelivery)

    Unknown macro: { $this->_linesToMail[] = $this->_formatter->format($event); }

    else

    Unknown macro: { $this->_sendLines(array($this->_formatter->format($event))); }

    }

    protected function _sendLines($lines)
    {
    $this->_mail->setBodyText(implode('', $this->_linesToMail));
    $this->_mail->send();
    }

    public function shutdown()
    {
    // If there are lines to mail, use them as message body.
    if (!empty($this->_linesToMail))

    Unknown macro: { // Set body text to all lines meant to be mailed, // then send message. $this->_sendLines($this->_linesToMail); }

    // Note that we don't destroy $this->_mail as the user may
    // want to use that object after mail sending.
    }
    ]]></ac:plain-text-body></ac:macro>

    <p>However, even if I prefer logging to syslog and let syslog monitoring software send all kind of notifications (mail, sms, jabber...) depending on log facility and other filters, I like this proposal! It could be helpful in a lot of places, mostly if your scripts are not running on your own servers!</p>

    <p>Best regards,<br />
    Thomas Gelf</p>

    1. Oct 24, 2008

      <p>Interesting point about long-running scripts; I didn't think of that. I can try to work this in soon.</p>

      <p>I'd like to mark this proposal as ready for recommendation soon, but I think this should be added first.</p>

      <p>Thanks! Will update it soon.</p>

  8. Sep 24, 2008

    <p>this proposal had lots of review, why not promote it to ready for recommendation? its quite important to be in a next stable release imho (maybe 1.7 already?)</p>

  9. Oct 24, 2008

    <p>So, I'd like to mark this proposal as ready for recommendation very soon. However, I have two action items that I'd like to take on before considering it final:</p>

    <p>1) Integrating optional use of Zend_Layout; would be nice to be able to use HTML-enabled emails with a header and a footer if the user so desires</p>

    <p>2) Integrating ability to send on-demand or deferred; primary use case supporting this is for use in a long-running script</p>

    <p>It's now 10/24; I'll aim to integrate these two items by 10/31 or 11/7, then mark this proposal as ready for recommendation.</p>

    <p>Thanks, everyone! Sorry I've been a slacker on this; it's been a crazy few months for me.</p>

    1. Oct 30, 2008

      <p>I've decided against pursuing the idea of "deferred" sending of messages vs. immediate.</p>

      <p>The main driver behind this is that _write() is called every time a message is logged. As such, you would send one email for each line.</p>

      <p>This makes sense in a long-running script that seldom generates errors, but I'm hesitant to include this for fear that it may be misused and misunderstood by a sizable portion of consumers.</p>

      <p>If anyone objects to this, please speak up.</p>

      <p>Otherwise, I'm integrating Zend_Layout usage now and will be updating the proposal this evening.</p>

  10. Oct 30, 2008

    <p>I have added the initial code showing Zend_Layout integration as of 10/30/2008. I have also added use case #04 outlining a simple Zend_Layout usage.</p>

    <p>I will leave this open for comments through 6:00 PM ET 11/03/2008, at which time I will be marking this proposal "Ready for Recommendation" barring any showstopper comments or problems.</p>

    <p>That's probably short notice, but I want to get this proposal into the meat of the review process sooner rather than later. Thanks, everyone!</p>

  11. Nov 05, 2008

    <p>Just a note that I want to refactor something related to my Zend_Layout integration; should be able to do that and adjust the proposal by 11/10.</p>

  12. Nov 15, 2008

    <p>I am now considering this proposal done and will mark it as Ready for Recommendation. Over the past two days, I have made the following changes:</p>

    <p>1) Added a setLayoutFormatter() method to allow for one formatter to be used with the plaintext email body, and a separate formatter to be used for the HTML email body. In the event that a specific formatter is now defined for the Zend_Layout entries, the default formatter will be used. The primary use case for this is when a plaintext email body needs its entry lines to end with PHP_EOL (newlines), but an HTML email body needs its lines to end in "<br/>".</p>

    <p>2) Added a setSubjectPrependText() method. Zend_Mail will only allow the subject to be set once, so it cannot be reset. Adding this method allows me to append the number of entries per-priority level to the mail subject upon shutdown(). For example, the consuming developer would NOT call setSubject() on the Zend_Mail object if they wanted to include the entry counts in the subject line. Instead, they would call something like "$mailWriter->setSubjectPrependText('My script')", which would end up rendering the subject line as "My script (WARN=X, ERR=Y, ...)". Use of this functionality is optional; the caller can choose to set a static subject on the mail message, but if they want the counts appended to the subject line, they can use this method.</p>

    <p>3) When using Zend_Layout, the layout templates must use "$this->_layout()->events" to render the entry lines from within the Zend_Layout templates.</p>

    <p>4) Added a private _getFormattedNumEntriesPerPriority() method that returns the entry count per-priority level for use in appending to the subject line, if applicable.</p>

    <p>These changes are a direct result of feedback received since the original proposal.</p>

    <p>As such, I am now marking it "Ready for Recommendation" and look forward to further feedback and its acceptance!</p>

  13. Dec 29, 2008

    <ac:macro ac:name="note"><ac:parameter ac:name="title">Zend Acceptance Comment</ac:parameter><ac:rich-text-body>
    <p>This proposal is accepted for immediate development in the standard library, as-is.</p></ac:rich-text-body></ac:macro>

  14. Jan 01, 2009

    <p>Just a note that unit tests and documentation were written and submitted to Matthew Weier O'Phinney on 1/1/2009. Awaiting review and/or committal to the repository.</p>

  15. Mar 18, 2009

    <p>I'm going to poke Matthew to see if there are any further tasks to be done.</p>

    1. Mar 18, 2009

      <p>It's already been moved into the standard library – unit tests and all – which Matthew has reviewed. It's done as far as I'm concerned; I've received no feedback or further guidance on it.</p>

      <p>I guess it would be released with 1.8.0, then?</p>

  16. Mar 18, 2009

    <p>Yes, it will be in 1.8. I just need to do the bookkeeping of moving it to the standard library section. I'll do that now.<br />
    Thanks for your contribution!</p>