[LOGBack-dev] svn commit: r484 - in logback/trunk: logback-classic/examples logback-classic/examples/classes logback-classic/src/main/java/ch/qos/logback/classic/helpers logback-classic/src/main/java/ch/qos/logback/classic/net logback-classic/src/test/java/ch/qos/logback/classic/net logback-site
noreply.seb at qos.ch
noreply.seb at qos.ch
Thu Aug 24 16:24:40 CEST 2006
Author: seb
Date: Thu Aug 24 16:24:39 2006
New Revision: 484
Added:
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/helpers/
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/helpers/CyclicBuffer.java
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/net/SMTPAppenderTest.java
Modified:
logback/trunk/logback-classic/examples/ (props changed)
logback/trunk/logback-classic/examples/classes/ (props changed)
logback/trunk/logback-site/ (props changed)
Log:
- SMTPAppender: on going work.
- A working version of this appender is commited. Reliable tests will follow.
- Minor ignore-list modifications.
Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/helpers/CyclicBuffer.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/helpers/CyclicBuffer.java Thu Aug 24 16:24:39 2006
@@ -0,0 +1,153 @@
+/**
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ *
+ * Copyright (C) 1999-2006, QOS.ch
+ *
+ * This library is free software, you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation.
+ */
+
+package ch.qos.logback.classic.helpers;
+
+import ch.qos.logback.classic.spi.LoggingEvent;
+
+
+/**
+
+ CyclicBuffer is used by other appenders to hold {@link LoggingEvent
+ LoggingEvents} for immediate or differed display.
+
+ <p>This buffer gives read access to any element in the buffer not
+ just the first or last element.
+
+ @author Ceki Gülcü
+ @since 0.9.0
+
+ */
+public class CyclicBuffer {
+
+ LoggingEvent[] ea;
+ int first;
+ int last;
+ int numElems;
+ int maxSize;
+
+ /**
+ Instantiate a new CyclicBuffer of at most <code>maxSize</code> events.
+
+ The <code>maxSize</code> argument must a positive integer.
+
+ @param maxSize The maximum number of elements in the buffer.
+ */
+ public CyclicBuffer(int maxSize) throws IllegalArgumentException {
+ if(maxSize < 1) {
+ throw new IllegalArgumentException("The maxSize argument ("+maxSize+
+ ") is not a positive integer.");
+ }
+ this.maxSize = maxSize;
+ ea = new LoggingEvent[maxSize];
+ first = 0;
+ last = 0;
+ numElems = 0;
+ }
+
+ /**
+ Add an <code>event</code> as the last event in the buffer.
+
+ */
+ public
+ void add(LoggingEvent event) {
+ ea[last] = event;
+ if(++last == maxSize)
+ last = 0;
+
+ if(numElems < maxSize)
+ numElems++;
+ else if(++first == maxSize)
+ first = 0;
+ }
+
+
+ /**
+ Get the <i>i</i>th oldest event currently in the buffer. If
+ <em>i</em> is outside the range 0 to the number of elements
+ currently in the buffer, then <code>null</code> is returned.
+
+
+ */
+ public
+ LoggingEvent get(int i) {
+ if(i < 0 || i >= numElems)
+ return null;
+
+ return ea[(first + i) % maxSize];
+ }
+
+ public
+ int getMaxSize() {
+ return maxSize;
+ }
+
+ /**
+ Get the oldest (first) element in the buffer. The oldest element
+ is removed from the buffer.
+ */
+ public
+ LoggingEvent get() {
+ LoggingEvent r = null;
+ if(numElems > 0) {
+ numElems--;
+ r = ea[first];
+ ea[first] = null;
+ if(++first == maxSize)
+ first = 0;
+ }
+ return r;
+ }
+
+ /**
+ Get the number of elements in the buffer. This number is
+ guaranteed to be in the range 0 to <code>maxSize</code>
+ (inclusive).
+ */
+ public
+ int length() {
+ return numElems;
+ }
+
+ /**
+ Resize the cyclic buffer to <code>newSize</code>.
+
+ @throws IllegalArgumentException if <code>newSize</code> is negative.
+ */
+ public
+ void resize(int newSize) {
+ if(newSize < 0) {
+ throw new IllegalArgumentException("Negative array size ["+newSize+
+ "] not allowed.");
+ }
+ if(newSize == numElems)
+ return; // nothing to do
+
+ LoggingEvent[] temp = new LoggingEvent[newSize];
+
+ int loopLen = newSize < numElems ? newSize : numElems;
+
+ for(int i = 0; i < loopLen; i++) {
+ temp[i] = ea[first];
+ ea[first] = null;
+ if(++first == numElems)
+ first = 0;
+ }
+ ea = temp;
+ first = 0;
+ numElems = loopLen;
+ maxSize = newSize;
+ if (loopLen == newSize) {
+ last = 0;
+ } else {
+ last = loopLen;
+ }
+ }
+}
Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java Thu Aug 24 16:24:39 2006
@@ -0,0 +1,399 @@
+/**
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ *
+ * Copyright (C) 1999-2006, QOS.ch
+ *
+ * This library is free software, you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation.
+ */
+
+package ch.qos.logback.classic.net;
+
+import java.io.File;
+import java.util.Date;
+import java.util.Properties;
+
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.helpers.CyclicBuffer;
+import ch.qos.logback.classic.spi.LoggingEvent;
+import ch.qos.logback.core.AppenderBase;
+import ch.qos.logback.core.Layout;
+import ch.qos.logback.core.rolling.TriggeringPolicy;
+import ch.qos.logback.core.util.OptionHelper;
+
+/**
+ * Send an e-mail when a specific logging event occurs, typically on errors or
+ * fatal 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.
+ *
+ * @author Ceki Gülcü
+ * @author Sébastien Pennec
+ *
+ * @since 1.0
+ */
+public class SMTPAppender extends AppenderBase {
+ private Layout layout;
+
+ private String to;
+ private String from;
+ private String subject;
+ private String smtpHost;
+ private int bufferSize = 512;
+ private boolean locationInfo = false;
+
+ protected CyclicBuffer cb = new CyclicBuffer(bufferSize);
+ protected Message msg;
+
+ protected TriggeringPolicy evaluator;
+
+ /**
+ * The default constructor will instantiate the appender with a
+ * {@link TriggeringEventEvaluator} that will trigger on events with level
+ * ERROR or higher.
+ */
+ public SMTPAppender() {
+ this(new DefaultEvaluator());
+ }
+
+ /**
+ * Use <code>evaluator</code> passed as parameter as the {@link
+ * TriggeringEventEvaluator} for this SMTPAppender.
+ */
+ public SMTPAppender(TriggeringPolicy evaluator) {
+ this.evaluator = evaluator;
+ }
+
+ /**
+ * Start the appender
+ */
+ public void start() {
+ Properties props = new Properties(System.getProperties());
+ if (smtpHost != null) {
+ props.put("mail.smtp.host", smtpHost);
+ }
+
+ Session session = Session.getInstance(props, null);
+ // session.setDebug(true);
+ msg = new MimeMessage(session);
+
+ try {
+ if (from != null) {
+ msg.setFrom(getAddress(from));
+ } else {
+ msg.setFrom();
+ }
+
+ msg.setRecipients(Message.RecipientType.TO, parseAddress(to));
+ if (subject != null) {
+ msg.setSubject(subject);
+ }
+
+ started = true;
+
+ } catch (MessagingException e) {
+ addError("Could not activate SMTPAppender options.", e);
+ }
+ }
+
+ /**
+ * 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.
+ */
+ protected void append(Object eventObject) {
+ LoggingEvent event = (LoggingEvent) eventObject;
+
+ if (!checkEntryConditions()) {
+ return;
+ }
+
+ event.getThreadName();
+ // event.getNDC();
+ // if (locationInfo) {
+ // event.getLocationInformation();
+ // }
+ cb.add(event);
+ //addInfo("Added event to the cyclic buffer: " + event.getMessage());
+
+ if (evaluator.isTriggeringEvent(null, 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.
+ */
+ protected boolean checkEntryConditions() {
+ if (this.msg == null) {
+ addError("Message object not configured.");
+ return false;
+ }
+
+ if (this.evaluator == null) {
+ addError("No TriggeringPolicy is set for appender [" + name + "].");
+ return false;
+ }
+
+ if (this.layout == null) {
+ addError("No layout set for appender named [" + name + "].");
+ return false;
+ }
+ return true;
+ }
+
+ synchronized public void stop() {
+ this.started = false;
+ }
+
+ InternetAddress getAddress(String addressStr) {
+ try {
+ return new InternetAddress(addressStr);
+ } catch (AddressException e) {
+ addError("Could not parse address [" + addressStr + "].", e);
+ return null;
+ }
+ }
+
+ InternetAddress[] parseAddress(String addressStr) {
+ try {
+ return InternetAddress.parse(addressStr, true);
+ } catch (AddressException e) {
+ addError("Could not parse address [" + addressStr + "].", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns value of the <b>To</b> option.
+ */
+ public String getTo() {
+ return to;
+ }
+
+ /**
+ * The <code>SMTPAppender</code> requires a {@link org.apache.log4j.Layout
+ * layout}.
+ */
+ 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 {
+ MimeBodyPart part = new MimeBodyPart();
+
+ StringBuffer sbuf = new StringBuffer();
+ String t = layout.getHeader();
+ if (t != null)
+ sbuf.append(t);
+ int len = cb.length();
+ for (int i = 0; i < len; i++) {
+ //sbuf.append(MimeUtility.encodeText(layout.format(cb.get())));
+ LoggingEvent event = cb.get();
+ sbuf.append(layout.doLayout(event));
+ // if (layout.ignoresThrowable()) {
+ // String[] s = event.getThrowableStrRep();
+ // if (s != null) {
+ // for (int j = 0; j < s.length; j++) {
+ // sbuf.append(s[j]);
+ // }
+ // }
+ // }
+ }
+ t = layout.getFooter();
+ if (t != null)
+ sbuf.append(t);
+ part.setContent(sbuf.toString(), "text/plain");
+
+ Multipart mp = new MimeMultipart();
+ mp.addBodyPart(part);
+ msg.setContent(mp);
+
+ msg.setSentDate(new Date());
+ Transport.send(msg);
+ } catch (Exception e) {
+ addError("Error occured while sending e-mail notification.", e);
+ }
+ }
+
+ /**
+ * Returns value of the <b>EvaluatorClass</b> option.
+ */
+ public String getEvaluatorClass() {
+ return evaluator == null ? null : evaluator.getClass().getName();
+ }
+
+ /**
+ * Returns value of the <b>From</b> option.
+ */
+ public String getFrom() {
+ return from;
+ }
+
+ /**
+ * Returns value of the <b>Subject</b> option.
+ */
+ public String getSubject() {
+ return subject;
+ }
+
+ /**
+ * The <b>From</b> option takes a string value which should be a e-mail
+ * address of the sender.
+ */
+ 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.
+ */
+ 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.
+ */
+ public void setBufferSize(int bufferSize) {
+ this.bufferSize = bufferSize;
+ cb.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.
+ */
+ 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 representing 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) {
+ try {
+ evaluator = (TriggeringPolicy) OptionHelper.instantiateByClassName(value,
+ TriggeringPolicy.class);
+ } catch (Exception ex) {
+ addError("Evaluator class instanciation failed");
+ }
+ }
+
+ /**
+ * 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.
+ */
+ public void setLocationInfo(boolean locationInfo) {
+ this.locationInfo = locationInfo;
+ }
+
+ /**
+ * Returns value of the <b>LocationInfo</b> option.
+ */
+ public boolean getLocationInfo() {
+ return locationInfo;
+ }
+
+ public Layout getLayout() {
+ return layout;
+ }
+
+ public void setLayout(Layout layout) {
+ this.layout = layout;
+ }
+}
+
+class DefaultEvaluator implements TriggeringPolicy {
+
+ private boolean started;
+
+ /**
+ * Is this <code>event</code> the e-mail triggering event?
+ *
+ * <p>
+ * This method returns <code>true</code>, if the event level has ERROR
+ * level or higher. Otherwise it returns <code>false</code>.
+ */
+ public boolean isTriggeringEvent(File file, Object eventObject) {
+ LoggingEvent event = (LoggingEvent) eventObject;
+ return event.getLevel().isGreaterOrEqual(Level.ERROR);
+ }
+
+ public boolean isStarted() {
+ return started == true;
+ }
+
+ public void start() {
+ started = true;
+ }
+
+ public void stop() {
+ started = false;
+ }
+}
Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/net/SMTPAppenderTest.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/net/SMTPAppenderTest.java Thu Aug 24 16:24:39 2006
@@ -0,0 +1,48 @@
+package ch.qos.logback.classic.net;
+
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.PatternLayout;
+import ch.qos.logback.core.Layout;
+import ch.qos.logback.core.util.StatusPrinter;
+
+public class SMTPAppenderTest {
+
+ public static void main(String[] args) {
+
+ Logger logger = (Logger) LoggerFactory.getLogger(SocketAppenderTest.class);
+ LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
+ SMTPAppender appender = new SMTPAppender();
+ appender.setContext(lc);
+ appender.setName("smtp");
+ appender.setFrom("user at host.dom");
+ appender.setLayout(buildLayout(lc));
+ appender.setSMTPHost("mail.qos.ch");
+ appender.setSubject("logging report");
+ appender.setTo("sebastien at qos.ch");
+
+ appender.start();
+
+ logger.addAppender(appender);
+
+ for (int i = 0; i <= 10; i++) {
+ logger.debug("** Hello world. n=" + i);
+ }
+ logger.error("Triggering request");
+
+ StatusPrinter.print(lc.getStatusManager());
+ }
+
+ private static Layout buildLayout(LoggerContext lc) {
+ PatternLayout layout = new PatternLayout();
+ layout.setContext(lc);
+ layout.setHeader("Some header\n");
+ layout.setPattern("%-4relative [%thread] %-5level %class - %msg%n");
+ layout.setFooter("Some footer");
+ layout.start();
+ return layout;
+ }
+
+}
More information about the logback-dev
mailing list