Thanks Jim for the code.

I've been looking at it and am a little confused.  In the doPatternSubst()
method, it looks like it is switching based upon a character.  Is this a
special 'cookie' type character that you are adding to your log message?
Can you give a little insight as to what is happening here?

Any backgound info would be appreciated.

Thanks,

Gray Jones

----- Original Message -----
From: "Jim Moore" <[EMAIL PROTECTED]>
To: "'LOG4J Users Mailing List'" <[EMAIL PROTECTED]>
Sent: Wednesday, May 30, 2001 11:16 AM
Subject: RE: dynamic subject with smtp appender


> You can't with what's in the disribution.  Here's one I wrote, though.
Take
> a look at doPatternSubst.
>
> -Jim Moore
>
> ===
>
> import org.apache.log4j.*;
> import org.apache.log4j.helpers.*;
> import org.apache.log4j.spi.*;
>
> import java.util.*;
>
> import javax.mail.*;
> import javax.mail.internet.*;
>
>
> /**
>  * Send an e-mail when a specific logging event occurs, typically on
> errors.<p>
>  *
>  * The number of logging events delivered in this e-mail depend on
>  *   the value of <b>BufferSize</b> option. The
>  *   <code>SMTPAppender</code> keeps only the last
>  *   <code>BufferSize</code> logging events in its cyclic buffer. This
>  *   keeps memory requirements at a reasonable level while still
>  *   delivering useful application context.
>  */
> public class SMTPAppender extends AppenderSkeleton {
>   private String to;
>   private String from;
>   private String subject;
>   private String smtpHost;
>   private int bufferSize = 512;
>
>   private CyclicBuffer _cb;
>   private boolean locationInfo = false;
>
>   protected TriggeringEventEvaluator evaluator;
>   private Session session;
>
>
>   /**
>    * The default constructor will instantiate the appender with a
>    * {@link TriggeringEventEvaluator} that will tirgger on events with
>    * priority ERROR or higher.
>    */
>   public SMTPAppender() {
>     this(new DefaultEvaluator());
>   }
>
>
>   /**
>    * Use <code>evaluator</code> passed as parameter as the {@link
>    * TriggeringEventEvaluator} for this SMTPAppender.
>    *
>    * @param evaluator
>    */
>   public SMTPAppender(TriggeringEventEvaluator evaluator) {
>     this.evaluator = evaluator;
>     setCyclicBuffer(new CyclicBuffer(bufferSize));
>   }
>
>
>   public void activeOptions() {
>     Properties props = System.getProperties();
>
>     if (smtpHost != null) {
>       props.put("mail.smtp.host", smtpHost);
>     }
>
>     session = Session.getDefaultInstance(System.getProperties(), null);
>     // session.setDebug(true);
>
>     super.activateOptions();
>   }
>
>
>   /**
>    * Perform SMTPAppender specific appending actions, mainly adding
>    * the event to a cyclic buffer and checking if the event triggers
>    * an e-mail to be sent.
>    *
>    * @param event
>    */
>   public void append(LoggingEvent event) {
>     if (!checkEntryConditions()) {
>       return;
>     }
>
>     event.getThreadName();
>     event.getNDC();
>     if (locationInfo) {
>       event.getLocationInformation();
>     }
>     getCyclicBuffer().add(event);
>     if (evaluator.isTriggeringEvent(event)) {
>       sendBuffer();
>     }
>   }
>
>   /**
>    * This method determines if there is a sense in attempting to append.
>    *
>    * <p>It checks whether there is a set output target and also if
>    * there is a set layout. If these checks fail, then the boolean
>    * value <code>false</code> is returned.
>    *
>    * @return
>    */
>   protected boolean checkEntryConditions() {
>     if (this.evaluator == null) {
>       errorHandler.error("No TriggeringEventEvaluator is set for appender
> ["+
>                          name+"].");
>       return false;
>     }
>
>
>     if (this.layout == null) {
>       errorHandler.error("No layout set for appender named ["+name+"].");
>       return false;
>     }
>     return true;
>   }
>
>
>   public synchronized void close() {
>     this.closed = true;
>   }
>
>
>   protected InternetAddress getAddress(String addressStr) {
>     try {
>       return new InternetAddress(addressStr);
>     }
>     catch (AddressException e) {
>       errorHandler.error("Could not parse address ["+addressStr+"].", e,
>                          ErrorCode.ADDRESS_PARSE_FAILURE);
>       return null;
>     }
>   }
>
>
>   protected InternetAddress[] parseAddress(String addressStr) {
>     try {
>       return InternetAddress.parse(addressStr, true);
>     }
>     catch (AddressException e) {
>       errorHandler.error("Could not parse address ["+addressStr+"].", e,
>                          ErrorCode.ADDRESS_PARSE_FAILURE);
>       return null;
>     }
>   }
>
>
>   /**
>    * Returns value of the <b>To</b> option.
>    *
>    * @return
>    */
>   public String getTo() {
>     return to;
>   }
>
>
>   /**
>    * The <code>SMTPAppender</code> requires a {@link Layout layout}.
>    *
>    * @return
>    */
>   public boolean requiresLayout() {
>     return true;
>   }
>
>
>   /**
>    * Send the contents of the cyclic buffer as an e-mail message.
>    */
>   protected void sendBuffer() {
>     // Note: this code already owns the monitor for this
>     // appender. This frees us from needing to synchronize on 'cb'.
>
>     try {
>       Message msg = createMessage();
>
>       try {
>         Transport.send(msg);
>       }
>       catch (MessagingException exp) {
>         LogLog.error("Error occured while sending e-mail notification.",
> exp);
>       }
>     }
>     catch (MessagingException exp) {
>       // the only thing that could've thrown this is createMessage()
>       LogLog.error("Could not create Message.", exp);
>     }
>   }
>
>
>   protected void setMessageFrom(Message msg) throws MessagingException {
>     if (from != null) {
>       msg.setFrom(getAddress(from));
>     }
>     else {
>       msg.setFrom();
>     }
>   }
>
>
>   protected void setMessageTo(Message msg) throws MessagingException {
>     msg.setRecipients(Message.RecipientType.TO, parseAddress(to));
>   }
>
>
>   protected void setMessageSubject(Message msg) throws MessagingException
{
>     if (subject != null) {
>       CyclicBuffer cb = getCyclicBuffer();
>       int length = cb.length();
>
>       String theSubject;
>       if (length > 0) {
>         theSubject = doPatternSubst(subject, cb.get(length-1));
>       }
>       else {
>         theSubject = subject;
>       }
>
>       msg.setSubject(theSubject);
>     }
>     else {
>       LogLog.warn("There is no subject set for emails");
>     }
>   }
>
>
>   public String doPatternSubst(String source, LoggingEvent evt) {
>     String str = source;
>     StringBuffer sBuf = new StringBuffer();
>
>     while (true) {
>       int index = str.indexOf('%');
>
>       if (index < 0 || index == str.length()) {
>         break;
>       }
>
>       char c = str.charAt(index+1);
>       switch (c) {
>         case 'm':
>           str = appendEventMessage(str, sBuf, index, evt);
>           break;
>         case 's':
>           str = appendExceptionMessage(str, sBuf, index, evt);
>           break;
>         case 'x':
>           str = appendExceptionClassName(str, sBuf, index, evt);
>           break;
>         default:
>           // nothing
>       }
>     }
>
>     sBuf.append(str);
>
>     return sBuf.toString();
>   }
>
>
>   /**
>    * Appends <tt>str</tt> to <tt>sBuf</tt> through the <tt>index</tt>
>    *   and then appends the Throwable class name that's in <tt>evt</tt>.
>    *
>    * @param str      the source string
>    *
>    * @param sBuf     the StringBuffer to append to
>    *
>    * @param index    the index to where the substitution parameter was
found
>    *
>    * @param evt      the LoggingEvent to get the Throwable from
>    *
>    * @return the portion of <tt>str</tt> after the substitution (never
null)
>    */
>   private static String appendExceptionClassName(String str, StringBuffer
> sBuf,
>                                                  int index, LoggingEvent
> evt) {
>     sBuf.append(str.substring(0, index));
>
>     ThrowableInformation info = evt.getThrowableInformation();
>
>     if (info != null) {
>       Throwable throwable = info.getThrowable();
>
>       if (throwable != null) {
>         sBuf.append(throwable.getClass().getName());
>       }
>     }
>
>     if (index+2 > str.length()) {
>       return "";
>     }
>
>     return str.substring(index+2);
>   }
>
>
>   /**
>    * Appends <tt>str</tt> to <tt>sBuf</tt> through the <tt>index</tt>
>    *   and then appends the Throwable message that's in <tt>evt</tt>.
>    *
>    * @param str      the source string
>    *
>    * @param sBuf     the StringBuffer to append to
>    *
>    * @param index    the index to where the substitution parameter was
found
>    *
>    * @param evt      the LoggingEvent to get the Throwable from
>    *
>    * @return the portion of <tt>str</tt> after the substitution (never
null)
>    */
>   private static String appendExceptionMessage(String str, StringBuffer
> sBuf,
>                                                int index, LoggingEvent
evt)
> {
>     sBuf.append(str.substring(0, index));
>
>     ThrowableInformation info = evt.getThrowableInformation();
>
>     if (info != null) {
>       Throwable throwable = info.getThrowable();
>
>       if (throwable != null) {
>         String msg = throwable.getMessage();
>         sBuf.append(msg == null ? "" : msg);
>       }
>     }
>
>     if (index+2 > str.length()) {
>       return "";
>     }
>
>     return str.substring(index+2);
>   }
>
>
>   /**
>    * Appends <tt>str</tt> to <tt>sBuf</tt> through the <tt>index</tt>
>    *   and then appends the message that's in <tt>evt</tt>.
>    *
>    * @param str      the source string
>    *
>    * @param sBuf     the StringBuffer to append to
>    *
>    * @param index    the index to where the substitution parameter was
found
>    *
>    * @param evt      the LoggingEvent to get the message from
>    *
>    * @return the portion of <tt>str</tt> after the substitution (never
null)
>    */
>   private static String appendEventMessage(String str, StringBuffer sBuf,
>                                            int index, LoggingEvent evt) {
>     sBuf.append(str.substring(0, index));
>
>     Object msg = evt.getMessage();
>     sBuf.append(msg == null ? "" : msg.toString());
>
>     if (index+2 > str.length()) {
>       return "";
>     }
>
>     return str.substring(index+2);
>   }
>
>
>   protected void addHeader(StringBuffer sbuf) {
>     String t = layout.getHeader();
>
>     if (t != null)
>       sbuf.append(t);
>   }
>
>
>   protected void addFooter(StringBuffer sbuf) {
>     String t = layout.getFooter();
>
>     if (t != null)
>       sbuf.append(t);
>   }
>
>
>   protected void addLogEvents(StringBuffer sbuf) {
>     CyclicBuffer cb = getCyclicBuffer();
>
>     int len =  cb.length();
>     for (int i = 0; i < len; i++) {
>       file://sbuf.append(MimeUtility.encodeText(layout.format(cb.get())));
>       LoggingEvent event = cb.get();
>       sbuf.append(layout.format(event));
>
>       if (layout.ignoresThrowable()) {
>         String[] s = event.getThrowableStrRep();
>
>         if (s != null) {
>           for (int j = 0; j < s.length; j++) {
>             sbuf.append(s[j]);
>           }
>         }
>       }
>     }
>   }
>
>
>   /**
>    */
>   protected Message createMessage() throws MessagingException {
>     // Note: this code already owns the monitor for this
>     // appender. This frees us from needing to synchronize on 'cb'.
>
>     MimeMessage msg = new MimeMessage(session);
>
>     setMessageFrom(msg);
>     setMessageTo(msg);
>     setMessageSubject(msg);
>
>     StringBuffer sbuf = new StringBuffer();
>
>     addHeader(sbuf);
>     addLogEvents(sbuf);
>     addFooter(sbuf);
>
>     MimeBodyPart part = new MimeBodyPart();
>     part.setContent(sbuf.toString(), layout.getContentType());
>
>     Multipart mp = new MimeMultipart();
>     mp.addBodyPart(part);
>
>     msg.setContent(mp);
>     msg.setSentDate(new Date());
>
>     return msg;
>   }
>
>
>   /**
>    * Sets the CyclicBuffer this will use for keeping track
>    *   of {@link LoggingEvent}s.
>    *
>    * @param cb     the CyclicBuffer to store the events in
>    */
>   protected void setCyclicBuffer(CyclicBuffer cb) {
>     _cb = cb;
>   }
>
>
>   /**
>    * Returns the CyclicBuffer the {@link LoggingEvent}s are stored in.
>    */
>   protected CyclicBuffer getCyclicBuffer() {
>     return _cb;
>   }
>
>
>   /**
>    * Returns value of the <b>EvaluatorClass</b> option.
>    *
>    * @return
>    */
>   public String getEvaluatorClass() {
>     return evaluator == null ? null : evaluator.getClass().getName();
>   }
>
>
>   /**
>    * Returns value of the <b>From</b> option.
>    *
>    * @return
>    */
>   public String getFrom() {
>     return from;
>   }
>
>   /**
>    * Returns value of the <b>Subject</b> option.
>    *
>    * @return
>    */
>   public String getSubject() {
>     return subject;
>   }
>
>
>   /**
>    * The <b>From</b> option takes a string value which should be a
>    * e-mail address of the sender.
>    *
>    * @param from
>    */
>   public void setFrom(String from) {
>     this.from = from;
>   }
>
>
>
>   /**
>    * The <b>Subject</b> option takes a string value which should be a
>    * the subject of the e-mail message.
>    *
>    * @param subject
>    */
>   public void setSubject(String subject) {
>     this.subject = subject;
>   }
>
>
>   /**
>    * The <b>BufferSize</b>option takes a positive integer
>    * representing the maximum number of logging events to collect in a
>    * cyclic buffer. When the <code>BufferSize</code> is reached,
>    * oldest events are deleted as new events are added to the
>    * buffer. By default the size of the cyclic buffer is 512 events.
>    *
>    * @param bufferSize
>    */
>   public void setBufferSize(int bufferSize) {
>     this.bufferSize = bufferSize;
>     getCyclicBuffer().resize(bufferSize);
>   }
>
>
>   /**
>    * The <b>SMTPHost</b> option takes a string value which should be a
>    * the host name of the SMTP server that will send the e-mail message.
>    *
>    * @param smtpHost
>    */
>   public void setSMTPHost(String smtpHost) {
>     this.smtpHost = smtpHost;
>   }
>
>
>   /**
>    * Returns value of the <b>SMTPHost</b> option.
>    */
>   public String getSMTPHost() {
>     return smtpHost;
>   }
>
>
>   /**
>    * The <b>To</b> option takes a string value which should be a
>    * comma separated list of e-mail address of the recipients.
>    */
>   public void setTo(String to) {
>     this.to = to;
>   }
>
>
>
>   /**
>    * Returns value of the <b>BufferSize</b> option.
>    */
>   public int getBufferSize() {
>     return bufferSize;
>   }
>
>   /**
>    * The <b>EvaluatorClass</b> option takes a string value
>    * repsenting the name of the class implementing the {@link
>    * TriggeringEventEvaluator} interface. A corresponding object will
>    * be instantiated and assigned as the triggering event evaluator
>    * for the SMTPAppender.
>    */
>   public void setEvaluatorClass(String value) {
>     evaluator = (TriggeringEventEvaluator)
>     OptionConverter.instantiateByClassName(value,
>                                            TriggeringEventEvaluator.class,
>                                            evaluator);
>   }
>
>
>   /**
>    * The <b>LocationInfo</b> option takes a boolean value. By
>    * default, it is set to false which means there will be no effort
>    * to extract the location information related to the event. As a
>    * result, the layout that formats the events as they are sent out
>    * in an e-mail is likely to place the wrong location information
>    * (if present in the format).
>    *
>    * <p>Location information extraction is comparatively very slow and
>    * should be avoided unless performance is not a concern.
>    *
>    * @param locationInfo
>    */
>   public void setLocationInfo(boolean locationInfo) {
>     this.locationInfo = locationInfo;
>   }
>
>
>   /**
>    * Returns value of the <b>LocationInfo</b> option.
>    *
>    * @return
>    */
>   public boolean getLocationInfo() {
>     return locationInfo;
>   }
>
>
>   protected static class DefaultEvaluator implements
> TriggeringEventEvaluator {
>     /**
>      * Is this <code>event</code> the e-mail triggering event?
>      *
>      * <p>This method returns <code>true</code>, if the event priority
>      * has ERROR priority or higher. Otherwisem it returns
>      * <code>false</code>.
>      *
>      * @param event
>      * @return
>      */
>     public boolean isTriggeringEvent(LoggingEvent event) {
>       return event.priority.isGreaterOrEqual(Priority.ERROR);
>     }
>   }
>
> }
>
>
>
> -----Original Message-----
> From: Gray Jones [mailto:[EMAIL PROTECTED]]
> Sent: Wednesday, May 30, 2001 11:05 AM
> To: LOG4J Users Mailing List
> Subject: dynamic subject with smtp appender
>
>
> Hello,
>
> Does anybody know if you can somehow dynamically create the subject field
of
> an smtp log?  For instance we are using an smtp appender to send emails of
> exceptions to the development group.  Our exception class has an id and
> description field which would be nice to include in the subject field of
the
> email message so we can sort, etc.
>
> Thanks,
>
> Gray Jones
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [EMAIL PROTECTED]
> For additional commands, e-mail: [EMAIL PROTECTED]
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [EMAIL PROTECTED]
> For additional commands, e-mail: [EMAIL PROTECTED]


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to