
Author: seb Date: Thu Oct 5 10:44:41 2006 New Revision: 625 Added: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/net/ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/net/SMTPAppender.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/CyclicBuffer.java Removed: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/helpers/CyclicBuffer.java Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java Log: - added SMTPAppender for access module - moved CyclicBuffer.java to the core module - modified classic module's SMTPAppender accordingly Added: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/net/SMTPAppender.java ============================================================================== --- (empty file) +++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/net/SMTPAppender.java Thu Oct 5 10:44:41 2006 @@ -0,0 +1,144 @@ +/** + * 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.access.net; + +import java.io.File; + +import ch.qos.logback.access.PatternLayout; +import ch.qos.logback.core.helpers.CyclicBuffer; +import ch.qos.logback.access.spi.AccessEvent; +import ch.qos.logback.core.Layout; +import ch.qos.logback.core.net.SMTPAppenderBase; +import ch.qos.logback.core.rolling.TriggeringPolicy; + +/** + * Send an e-mail when a specific access event occurs, typically on errors or + * fatal errors. + * + * <p> + * The number of access 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> access events in its cyclic buffer. This + * keeps memory requirements at a reasonable level while still delivering useful + * application context. + * <p> + * By default, the email is sent everything an event has a status code of + * 500 (server error) or higher. + * <p> + * @author Ceki Gülcü + * @author Sébastien Pennec + * + */ +public class SMTPAppender extends SMTPAppenderBase { + + static final String DEFAULT_SUBJECT_PATTERN = "%m"; + + private int bufferSize = 512; + protected CyclicBuffer cb = new CyclicBuffer(bufferSize); + + /** + * 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; + } + + /** + * Perform SMTPAppender specific appending actions, mainly adding the event to + * a cyclic buffer. + */ + protected void subAppend(Object eventObject) { + AccessEvent event = (AccessEvent) eventObject; + + cb.add(event); + // addInfo("Added event to the cyclic buffer: " + event.getMessage()); + } + + @Override + protected void fillBuffer(StringBuffer sbuf) { + int len = cb.length(); + for (int i = 0; i < len; i++) { + // sbuf.append(MimeUtility.encodeText(layout.format(cb.get()))); + Object event = cb.get(); + sbuf.append(layout.doLayout(event)); + } + } + + /** + * 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); + } + + /** + * Returns value of the <b>BufferSize</b> option. + */ + public int getBufferSize() { + return bufferSize; + } + + @Override + protected Layout makeSubjectLayout(String subjectStr) { + if(subjectStr == null) { + subjectStr = DEFAULT_SUBJECT_PATTERN; + } + PatternLayout pl = new PatternLayout(); + pl.setPattern(subjectStr); + pl.start(); + return pl; + } +} + +class DefaultEvaluator implements TriggeringPolicy { + + private boolean started; + + private static final Integer TRIGGERING_STATUS_CODE = 500; + /** + * Is this <code>event</code> the e-mail triggering event? + * + * <p> + * This method returns <code>true</code>, if the event status code + * is 500 (server error) or higher. Otherwise it returns <code>false</code>. + */ + public boolean isTriggeringEvent(File file, Object eventObject) { + AccessEvent event = (AccessEvent) eventObject; + return TRIGGERING_STATUS_CODE.compareTo(event.getStatusCode()) <= 0; + } + + public boolean isStarted() { + return started == true; + } + + public void start() { + started = true; + } + + public void stop() { + started = false; + } +} Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java ============================================================================== --- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java (original) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java Thu Oct 5 10:44:41 2006 @@ -14,7 +14,7 @@ import ch.qos.logback.classic.Level; import ch.qos.logback.classic.PatternLayout; -import ch.qos.logback.classic.helpers.CyclicBuffer; +import ch.qos.logback.core.helpers.CyclicBuffer; import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.core.Layout; import ch.qos.logback.core.net.SMTPAppenderBase; Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/CyclicBuffer.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/CyclicBuffer.java Thu Oct 5 10:44:41 2006 @@ -0,0 +1,142 @@ +/** + * 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.core.helpers; + + +/** + * + * CyclicBuffer is used by other appenders to hold + * objects 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ü + */ +public class CyclicBuffer { + + Object[] 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 Object[maxSize]; + first = 0; + last = 0; + numElems = 0; + } + + /** + * Add an <code>event</code> as the last event in the buffer. + * + */ + public void add(Object 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 Object 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 Object get() { + Object 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 + + Object[] temp = new Object[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; + } + } +}