[LOGBack-dev] svn commit: r559 - in logback/trunk: . logback-classic logback-classic/src/main/java/ch/qos/logback/classic logback-classic/src/main/java/ch/qos/logback/classic/helpers logback-classic/src/main/java/ch/qos/logback/classic/html logback-classic/src/main/java/ch/qos/logback/classic/net logback-classic/src/main/java/ch/qos/logback/classic/pattern logback-classic/src/main/java/ch/qos/logback/classic/spi logback-classic/src/test/java/ch/qos/logback/classic/html
noreply.seb at qos.ch
noreply.seb at qos.ch
Tue Sep 12 09:35:04 CEST 2006
Author: seb
Date: Tue Sep 12 09:35:04 2006
New Revision: 559
Added:
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/helpers/Transform.java
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/HTMLLayout.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/html/
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java
Modified:
logback/trunk/logback-classic/pom.xml
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/PatternLayout.java
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/pattern/ThrowableHandlingConverter.java
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEvent.java
logback/trunk/pom.xml
Log:
- added HTMLLayout, work in progress
- defaultConverterMap in PatternLayout now is public, to allow sharing of the map with HTMLLayout
- added a Dom4j dependency for Classic tests, in pom.xml
- a few re-format of classes, replacing tabs by spaces
Modified: logback/trunk/logback-classic/pom.xml
==============================================================================
--- logback/trunk/logback-classic/pom.xml (original)
+++ logback/trunk/logback-classic/pom.xml Tue Sep 12 09:35:04 2006
@@ -42,7 +42,13 @@
<artifactId>mail</artifactId>
<scope>compile</scope>
</dependency>
-
+
+ <dependency>
+ <groupId>dom4j</groupId>
+ <artifactId>dom4j</artifactId>
+ <scope>test</scope>
+ </dependency>
+
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/PatternLayout.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/PatternLayout.java (original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/PatternLayout.java Tue Sep 12 09:35:04 2006
@@ -38,7 +38,7 @@
// FIXME fix exception handling
- static final Map<String, String> defaultConverterMap = new HashMap<String, String>();
+ public static final Map<String, String> defaultConverterMap = new HashMap<String, String>();
static {
Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/helpers/Transform.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/helpers/Transform.java Tue Sep 12 09:35:04 2006
@@ -0,0 +1,98 @@
+/**
+ * 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;
+
+/**
+ * Utility class for transforming strings.
+ *
+ * @author Ceki Gülcü
+ * @author Michael A. McAngus
+ */
+public class Transform {
+ private static final String CDATA_START = "<![CDATA[";
+ private static final String CDATA_END = "]]>";
+ private static final String CDATA_PSEUDO_END = "]]>";
+ private static final String CDATA_EMBEDED_END = CDATA_END + CDATA_PSEUDO_END
+ + CDATA_START;
+ private static final int CDATA_END_LEN = CDATA_END.length();
+
+ /**
+ * This method takes a string which may contain HTML tags (ie, <b>,
+ * <table>, etc) and replaces any '<' and '>' characters with
+ * respective predefined entity references.
+ *
+ * @param input
+ * The text to be converted.
+ */
+ public static String escapeTags(final String input) {
+ // Check if the string is null or zero length -- if so, return
+ // what was sent in.
+ if ((input == null) || (input.length() == 0)
+ || (input.indexOf("<") == -1 && input.indexOf(">") == -1)) {
+ return input;
+ }
+
+ StringBuffer buf = new StringBuffer(input);
+ for (int i = 0; i < buf.length(); i++) {
+ char ch = buf.charAt(i);
+ if (ch == '<') {
+ buf.replace(i, i + 1, "<");
+ } else if (ch == '>') {
+ buf.replace(i, i + 1, ">");
+ }
+ }
+ return buf.toString();
+ }
+
+ // public static void appendEscapingCDATA(StringBuffer buf, String str) {
+ //
+ // }
+
+ /**
+ * Ensures that embeded CDEnd strings (]]>) are handled properly within
+ * message, NDC and throwable tag text.
+ *
+ * @param output
+ * Writer. The initial CDSutart (<![CDATA[) and final CDEnd (]]>) of
+ * the CDATA section are the responsibility of the calling method.
+ *
+ * @param str
+ * The String that is inserted into an existing CDATA Section.
+ */
+ public static void appendEscapingCDATA(StringBuffer output, String str) {
+ if (str == null) {
+ return;
+ }
+
+ int end = str.indexOf(CDATA_END);
+
+ if (end < 0) {
+ output.append(str);
+
+ return;
+ }
+
+ int start = 0;
+
+ while (end > -1) {
+ output.append(str.substring(start, end));
+ output.append(CDATA_EMBEDED_END);
+ start = end + CDATA_END_LEN;
+
+ if (start < str.length()) {
+ end = str.indexOf(CDATA_END, start);
+ } else {
+ return;
+ }
+ }
+
+ output.append(str.substring(start));
+ }
+}
Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/HTMLLayout.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/HTMLLayout.java Tue Sep 12 09:35:04 2006
@@ -0,0 +1,424 @@
+/**
+ * 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.html;
+
+import ch.qos.logback.classic.ClassicLayout;
+import ch.qos.logback.classic.PatternLayout;
+import ch.qos.logback.classic.helpers.Transform;
+import ch.qos.logback.classic.pattern.NopThrowableInformationConverter;
+import ch.qos.logback.classic.pattern.ThrowableHandlingConverter;
+import ch.qos.logback.classic.pattern.ThrowableInformationConverter;
+import ch.qos.logback.classic.spi.LoggingEvent;
+import ch.qos.logback.classic.spi.ThrowableInformation;
+import ch.qos.logback.core.LayoutBase;
+import ch.qos.logback.core.pattern.Converter;
+import ch.qos.logback.core.pattern.DynamicConverter;
+import ch.qos.logback.core.pattern.parser.Node;
+import ch.qos.logback.core.pattern.parser.Parser;
+import ch.qos.logback.core.pattern.parser.ScanException;
+
+/**
+ *
+ * HTMLLayout outputs events in an HTML table. The content of the table columns
+ * are specified using a conversion pattern. See
+ * {@link ch.qos.logback.classic.PatternLayout} for documentation on the
+ * available patterns.
+ *
+ * @author Ceki Gülcü
+ * @author Sébastien Pennec
+ */
+public class HTMLLayout extends LayoutBase implements ClassicLayout {
+
+ /**
+ * Default pattern string for log output. Currently set to the string <b>"%m"
+ * </b> which just prints the application supplied message.
+ */
+ static final String DEFAULT_CONVERSION_PATTERN = "%date%thread%level%logger%mdc%msg";
+
+ static final String TRACE_PREFIX = "<br /> ";
+ protected final int BUF_SIZE = 256;
+ protected final int MAX_CAPACITY = 1024;
+
+ private String pattern;
+
+ private Converter head;
+
+ //private String timezone;
+ private String title = "Logback Log Messages";
+ private boolean locationInfo;
+
+ private boolean internalCSS = false;
+ private String url2ExternalCSS = "http://logging.apache.org/log4j/docs/css/eventTable-1.0.css";
+
+ // Does our PatternConverter chain handle throwable on its own?
+ private boolean chainHandlesThrowable;
+
+ // counter keeping track of the rows output
+ private long counter = 0;
+
+ /**
+ * Constructs a PatternLayout using the DEFAULT_LAYOUT_PATTERN.
+ *
+ * The default pattern just produces the application supplied message.
+ */
+ public HTMLLayout() {
+ pattern = DEFAULT_CONVERSION_PATTERN;
+ }
+
+ /**
+ * Set the <b>ConversionPattern </b> option. This is the string which controls
+ * formatting and consists of a mix of literal content and conversion
+ * specifiers.
+ */
+ public void setPattern(String conversionPattern) {
+ pattern = conversionPattern;
+ }
+
+ /**
+ * Returns the value of the <b>ConversionPattern </b> option.
+ */
+ public String getPattern() {
+ return pattern;
+ }
+
+ /**
+ * Parses the pattern and creates the Converter linked list.
+ */
+ public void start() {
+ try {
+ Parser p = new Parser(pattern);
+ if (getContext() != null) {
+ p.setStatusManager(getContext().getStatusManager());
+ }
+ Node t = p.parse();
+ this.head = p.compile(t, PatternLayout.defaultConverterMap);
+ postCompileProcessing(head);
+ DynamicConverter.startConverters(this.head);
+ } catch (ScanException ex) {
+ addError("Incorrect pattern found", ex);
+ }
+
+ started = true;
+ }
+
+ private void postCompileProcessing(Converter c) {
+ while (c != null) {
+ if (c instanceof ThrowableHandlingConverter) {
+ chainHandlesThrowable = true;
+ }
+ c = c.getNext();
+ }
+ chainHandlesThrowable = false;
+ }
+
+ /**
+ * The <b>Title </b> option takes a String value. This option sets the
+ * document title of the generated HTML document.
+ *
+ * <p>
+ * Defaults to 'Logback Log Messages'.
+ */
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ /**
+ * Returns the current value of the <b>Title </b> option.
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ /**
+ * Returns the value of the internalCSS option. See {@link #setInternalCSS}
+ * method for details about the meaning of this option.
+ *
+ * @return boolean Value of internalCSS option
+ */
+ public boolean isInternalCSS() {
+ return internalCSS;
+ }
+
+ /**
+ * Set the value of the internalCSS option. If set to true, the generated HTML
+ * ouput will include an internal cascading style sheet. Otherwise, the
+ * generated HTML output will include a reference to an external CSS.
+ * <p>
+ * By default, <code>internalCSS</code> value is set to false, that is, by
+ * default, only a link to an external CSS file will be generated.
+ *
+ * @see #setURL2ExternalCSS
+ *
+ * @param internalCSS
+ */
+ public void setInternalCSS(boolean internalCSS) {
+ this.internalCSS = internalCSS;
+ }
+
+ /**
+ * Return the URL to the external CSS file. See {@link #setURL2ExternalCSS}
+ * method for details about the meaning of this option.
+ *
+ * @return URL to the external CSS file.
+ */
+ public String getURL2ExternalCSS() {
+ return url2ExternalCSS;
+ }
+
+ /**
+ * Set the URL for the external CSS file. By default, the external CSS file is
+ * set to "http://logging.apache.org/log4j/docs/css/eventTable-1.0.css".
+ */
+ public void setURL2ExternalCSS(String url2ExternalCSS) {
+ this.url2ExternalCSS = url2ExternalCSS;
+ }
+
+ /**
+ * Returns the content type output by this layout, i.e "text/html".
+ */
+ public String getContentType() {
+ return "text/html";
+ }
+
+ void appendThrowableAsHTML(final String[] s, final StringBuffer sbuf) {
+ if (s != null) {
+ int len = s.length;
+ if (len == 0) {
+ return;
+ }
+ sbuf.append(Transform.escapeTags(s[0]));
+ sbuf.append(LINE_SEP);
+ for (int i = 1; i < len; i++) {
+ sbuf.append(TRACE_PREFIX);
+ sbuf.append(Transform.escapeTags(s[i]));
+ sbuf.append(LINE_SEP);
+ }
+ }
+ }
+
+ /**
+ * Returns appropriate HTML headers.
+ */
+ public String getHeader() {
+ StringBuffer sbuf = new StringBuffer();
+ sbuf.append("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"");
+ sbuf.append(" \"http://www.w3.org/TR/html4/loose.dtd\">");
+ sbuf.append(LINE_SEP);
+ sbuf.append("<html>");
+ sbuf.append(LINE_SEP);
+ sbuf.append("<head>");
+ sbuf.append(LINE_SEP);
+ sbuf.append("<title>");
+ sbuf.append(title);
+ sbuf.append("</title>");
+ sbuf.append(LINE_SEP);
+ if (internalCSS) {
+ getInternalCSS(sbuf);
+ } else {
+ sbuf.append("<LINK REL=StyleSheet HREF=\"");
+ sbuf.append(url2ExternalCSS);
+ sbuf.append("\" TITLE=\"Basic\">");
+ }
+ sbuf.append(LINE_SEP);
+ sbuf.append("</head>");
+ sbuf.append(LINE_SEP);
+ sbuf.append("<body>");
+ sbuf.append(LINE_SEP);
+
+ sbuf.append("<hr size=\"1\" noshade>");
+ sbuf.append(LINE_SEP);
+
+ sbuf.append("Log session start time ");
+ sbuf.append(new java.util.Date());
+ sbuf.append("<br>");
+ sbuf.append(LINE_SEP);
+ sbuf.append("<br>");
+ sbuf.append(LINE_SEP);
+ sbuf.append("<table cellspacing=\"0\">");
+ sbuf.append(LINE_SEP);
+
+ sbuf.append("<tr class=\"header\">");
+ sbuf.append(LINE_SEP);
+
+ Converter c = head;
+ String name;
+ while (c != null) {
+ name = computeConverterName(c);
+ if (name == null) {
+ c = c.getNext();
+ continue;
+ }
+ sbuf.append("<td class=\"");
+ sbuf.append(computeConverterName(c));
+ sbuf.append("\">");
+ sbuf.append("<td>");
+ sbuf.append(computeConverterName(c));
+ sbuf.append("</td>");
+ sbuf.append(LINE_SEP);
+ c = c.getNext();
+ }
+ sbuf.append("</tr>");
+ sbuf.append(LINE_SEP);
+
+ return sbuf.toString();
+ }
+
+ /**
+ * Returns the appropriate HTML footers.
+ */
+ public String getFooter() {
+ StringBuffer sbuf = new StringBuffer();
+ sbuf.append("</table>");
+ sbuf.append(LINE_SEP);
+ sbuf.append("<br>");
+ sbuf.append(LINE_SEP);
+ sbuf.append("</body></html>");
+ return sbuf.toString();
+ }
+
+ /**
+ * The HTML layout handles the throwable contained in logging events. Hence,
+ * this method return <code>false</code>.
+ */
+ public boolean ignoresThrowable() {
+ return false;
+ }
+
+ public String doLayout(Object event) {
+ return doLayout((LoggingEvent) event);
+ }
+
+ public String doLayout(LoggingEvent event) {
+
+ boolean odd = true;
+ if (((counter++) & 1) == 0) {
+ odd = false;
+ }
+
+ String level = event.getLevel().toString().toLowerCase();
+
+ StringBuffer buf = new StringBuffer();
+ buf.append(LINE_SEP);
+ buf.append("<tr class=\"");
+ buf.append(level);
+ if (odd) {
+ buf.append(" odd\">");
+ } else {
+ buf.append(" even\">");
+ }
+ buf.append(LINE_SEP);
+
+ Converter c = head;
+ while (c != null) {
+ if (c instanceof ThrowableHandlingConverter) {
+ ThrowableHandlingConverter converter = (ThrowableHandlingConverter)c;
+ if (converter.onNewLine(event)) {
+ buf.append("</tr>");
+ buf.append("<tr>");
+ appendEventToBuffer(buf, c, event);
+ if (c.getNext() != null) {
+ //here we assume that when we exist the while loop,
+ //a </tr> tag is added.
+ buf.append("</tr>");
+ buf.append("<tr>");
+ }
+ }
+ } else {
+ appendEventToBuffer(buf, c, event);
+ }
+ c = c.getNext();
+ }
+ buf.append("</tr>");
+ buf.append(LINE_SEP);
+
+ // if the pattern chain handles throwables then no need to do it again here.
+ if (!chainHandlesThrowable) {
+ ThrowableInformation ti = event.getThrowableInformation();
+ if (ti != null) {
+ String[] s = ti.getThrowableStrRep();
+ if (s != null) {
+ buf.append("<tr><td class=\"Exception\" colspan=\"6\">");
+ appendThrowableAsHTML(s, buf);
+ buf.append("</td></tr>");
+ buf.append(LINE_SEP);
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ private void appendEventToBuffer(StringBuffer buf, Converter c, LoggingEvent event) {
+ buf.append("<td class=\"");
+ buf.append(computeConverterName(c));
+ buf.append("\">");
+ buf.append(c.convert(event));
+ buf.append("</td>");
+ buf.append(LINE_SEP);
+ }
+
+ /**
+ * Generate an internal CSS file.
+ *
+ * @param buf The StringBuffer where the CSS file will be placed.
+ */
+ void getInternalCSS(StringBuffer buf) {
+
+ buf.append("<STYLE type=\"text/css\">");
+ buf.append(LINE_SEP);
+ buf.append("table { margin-left: 2em; margin-right: 2em; border-left: 2px solid #AAA; }");
+ buf.append(LINE_SEP);
+
+ buf.append("TR.even { background: #FFFFFF; }");
+ buf.append(LINE_SEP);
+
+ buf.append("TR.odd { background: #DADADA; }");
+ buf.append(LINE_SEP);
+
+ buf.append("TR.warn TD.level, TR.error TD.level, TR.fatal TD.level {font-weight: bold; color: #FF4040 }");
+ buf.append(LINE_SEP);
+
+ buf.append("TD { padding-right: 1ex; padding-left: 1ex; border-right: 2px solid #AAA; }");
+ buf.append(LINE_SEP);
+
+ buf.append("TD.Time, TD.Date { text-align: right; font-family: courier, monospace; font-size: smaller; }");
+ buf.append(LINE_SEP);
+
+ buf.append("TD.Thread { text-align: left; }");
+ buf.append(LINE_SEP);
+
+ buf.append("TD.Level { text-align: right; }");
+ buf.append(LINE_SEP);
+
+ buf.append("TD.Logger { text-align: left; }");
+ buf.append(LINE_SEP);
+
+ buf.append("TR.header { background: #9090FF; color: #FFF; font-weight: bold; font-size: larger; }");
+ buf.append(LINE_SEP);
+
+ buf.append("TD.Exception { background: #C0C0F0; font-family: courier, monospace;}");
+ buf.append(LINE_SEP);
+
+ buf.append("</STYLE>");
+ buf.append(LINE_SEP);
+
+ }
+
+ private String computeConverterName(Converter c) {
+ String className = c.getClass().getSimpleName();
+ int index = className.indexOf("Converter");
+ if (index == -1) {
+ return className;
+ } else {
+ return className.substring(0, index);
+ }
+ }
+
+}
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 Tue Sep 12 09:35:04 2006
@@ -50,342 +50,341 @@
* @since 1.0
*/
public class SMTPAppender extends AppenderBase {
- private Layout layout;
+ 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;
- }
-
- /**
- * 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;
- }
+ 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();
+ // 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;
+ }
+
+ /**
+ * 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;
+ 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;
- }
+ /**
+ * 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;
+ }
}
Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableHandlingConverter.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableHandlingConverter.java (original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableHandlingConverter.java Tue Sep 12 09:35:04 2006
@@ -1,6 +1,7 @@
package ch.qos.logback.classic.pattern;
import ch.qos.logback.classic.pattern.ClassicConverter;
+import ch.qos.logback.classic.spi.LoggingEvent;
/**
@@ -12,4 +13,9 @@
boolean handlesThrowable() {
return true;
}
+
+ // tentatively...
+ public boolean onNewLine(LoggingEvent le) {
+ return le.getThrowableInformation() != null;
+ }
}
Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEvent.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEvent.java (original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEvent.java Tue Sep 12 09:35:04 2006
@@ -41,247 +41,247 @@
*/
public class LoggingEvent implements Serializable {
- /**
- *
- */
- private static final long serialVersionUID = 3022264832697160750L;
-
- /**
- *
- */
- private static long startTime = System.currentTimeMillis();
-
- /**
- * Fully qualified name of the calling Logger class. This field does not
- * survive serialization.
- *
- * <p>
- * Note that the getCallerInformation() method relies on this fact.
- */
- transient String fqnOfLoggerClass;
-
- /**
- * The name of thread in which this logging event was generated.
- */
- private String threadName;
-
- /**
- * Level of logging event.
- *
- * <p>
- * This field should not be accessed directly. You shoud use the {@link
- * #getLevel} method instead.
- * </p>
- *
- */
- private transient Level level;
-
- private String message;
- private String formattedMessage;
-
- private Object[] argumentArray;
-
- private ThrowableInformation throwableInfo;
-
- private CallerData[] callerDataArray;
- private LoggerRemoteView loggerRemoteView;
-
- private Marker marker;
-
- private Map<String, String> mdcPropertyMap;
-
- /**
- * The number of milliseconds elapsed from 1/1/1970 until logging event was
- * created.
- */
- private long timeStamp;
-
- public LoggingEvent() {
- }
-
- public LoggingEvent(String fqcn, Logger logger, Level level, String message,
- Throwable throwable, Object[] argArray) {
- this.fqnOfLoggerClass = fqcn;
- this.loggerRemoteView = logger.getLoggerRemoteView();
- this.level = level;
- this.message = message;
-
- if (throwable != null) {
- this.throwableInfo = new ThrowableInformation(throwable);
- }
-
- if (argArray != null) {
- formattedMessage = MessageFormatter.arrayFormat(message, argArray);
- } else {
- formattedMessage = message;
- }
- timeStamp = System.currentTimeMillis();
-
- mdcPropertyMap = MDC.getPropertyMap();
- }
-
- public void setArgumentArray(Object[] argArray) {
- if (this.argumentArray != null) {
- throw new IllegalStateException("argArray has been already set");
- }
- this.argumentArray = argArray;
- }
-
- public Object[] getArgumentArray() {
- return this.argumentArray;
- }
-
- public Level getLevel() {
- return level;
- }
-
- public String getThreadName() {
- if (threadName == null) {
- threadName = (Thread.currentThread()).getName();
- }
- return threadName;
- }
-
- /**
- * @param threadName
- * The threadName to set.
- * @throws IllegalStateException
- * If threadName has been already set.
- */
- public void setThreadName(String threadName) throws IllegalStateException {
- if (this.threadName != null) {
- throw new IllegalStateException("threadName has been already set");
- }
- this.threadName = threadName;
- }
-
- /**
- * Returns the throwable information contained within this event. May be
- * <code>null</code> if there is no such information.
- */
- public ThrowableInformation getThrowableInformation() {
- return throwableInfo;
- }
-
- /**
- * Set this event's throwable information.
- */
- public void setThrowableInformation(ThrowableInformation ti) {
- if (throwableInfo != null) {
- throw new IllegalStateException(
- "ThrowableInformation has been already set.");
- } else {
- throwableInfo = ti;
- }
- }
-
- /**
- * This method should be called prior to serializing an event. It should also
- * be called when using asynchronous logging.
- */
- public void prepareForDeferredProcessing() {
- this.getThreadName();
- }
-
- public LoggerRemoteView getLoggerRemoteView() {
- return loggerRemoteView;
- }
-
- public void setLoggerRemoteView(LoggerRemoteView loggerRemoteView) {
- this.loggerRemoteView = loggerRemoteView;
- }
-
- public String getMessage() {
- return message;
- }
-
- public void setMessage(String message) {
- if (this.message != null) {
- throw new IllegalStateException(
- "The message for this event has been set already.");
- }
- this.message = message;
- }
-
- public long getTimeStamp() {
- return timeStamp;
- }
-
- public void setTimeStamp(long timeStamp) {
- this.timeStamp = timeStamp;
- }
-
- public void setLevel(Level level) {
- if (this.level != null) {
- throw new IllegalStateException(
- "The level has been already set for this event.");
- }
- this.level = level;
- }
-
- /**
- * The time at which this class was loaded into memory, expressed in
- * millisecond elapsed since the epoch (1.1.1970).
- *
- * @return The time as measured when this class was loaded into memory.
- */
- public static final long getStartTime() {
- return startTime;
- }
-
- /**
- * Get the caller information for this logging event. If caller information is
- * null at the time of its invocation, this method extracts location
- * information. The collected information is cached for future use.
- *
- * <p>
- * Note that after serialization it is impossible to correctly extract caller
- * information.
- * </p>
- */
- public CallerData[] getCallerData() {
- // we rely on the fact that fqnOfLoggerClass does not survive
- // serialization
- if (callerDataArray == null && fqnOfLoggerClass != null) {
- callerDataArray = CallerData.extract(new Throwable(), fqnOfLoggerClass);
- }
- return callerDataArray;
- }
-
- public void setCallerInformation(CallerData[] callerDataArray) {
- this.callerDataArray = callerDataArray;
- }
-
- public Marker getMarker() {
- return marker;
- }
-
- public void setMarker(Marker marker) {
- if (this.marker != null) {
- throw new IllegalStateException(
- "The marker has been already set for this event.");
- }
- this.marker = marker;
- }
-
- public String getFormattedMessage() {
- return formattedMessage;
- }
-
- public Map<String, String> getMDCPropertyMap() {
- return mdcPropertyMap;
- }
-
- private void writeObject(ObjectOutputStream out) throws IOException {
- out.defaultWriteObject();
- out.writeInt(level.levelInt);
- }
-
- private void readObject(ObjectInputStream in) throws IOException,
- ClassNotFoundException {
- in.defaultReadObject();
- int levelInt = in.readInt();
- level = Level.toLevel(levelInt);
- }
+ /**
+ *
+ */
+ private static final long serialVersionUID = 3022264832697160750L;
+
+ /**
+ *
+ */
+ private static long startTime = System.currentTimeMillis();
+
+ /**
+ * Fully qualified name of the calling Logger class. This field does not
+ * survive serialization.
+ *
+ * <p>
+ * Note that the getCallerInformation() method relies on this fact.
+ */
+ transient String fqnOfLoggerClass;
+
+ /**
+ * The name of thread in which this logging event was generated.
+ */
+ private String threadName;
+
+ /**
+ * Level of logging event.
+ *
+ * <p>
+ * This field should not be accessed directly. You shoud use the {@link
+ * #getLevel} method instead.
+ * </p>
+ *
+ */
+ private transient Level level;
+
+ private String message;
+ private String formattedMessage;
+
+ private Object[] argumentArray;
+
+ private ThrowableInformation throwableInfo;
+
+ private CallerData[] callerDataArray;
+ private LoggerRemoteView loggerRemoteView;
+
+ private Marker marker;
+
+ private Map<String, String> mdcPropertyMap;
+
+ /**
+ * The number of milliseconds elapsed from 1/1/1970 until logging event was
+ * created.
+ */
+ private long timeStamp;
+
+ public LoggingEvent() {
+ }
+
+ public LoggingEvent(String fqcn, Logger logger, Level level, String message,
+ Throwable throwable, Object[] argArray) {
+ this.fqnOfLoggerClass = fqcn;
+ this.loggerRemoteView = logger.getLoggerRemoteView();
+ this.level = level;
+ this.message = message;
+
+ if (throwable != null) {
+ this.throwableInfo = new ThrowableInformation(throwable);
+ }
+
+ if (argArray != null) {
+ formattedMessage = MessageFormatter.arrayFormat(message, argArray);
+ } else {
+ formattedMessage = message;
+ }
+ timeStamp = System.currentTimeMillis();
+
+ mdcPropertyMap = MDC.getPropertyMap();
+ }
+
+ public void setArgumentArray(Object[] argArray) {
+ if (this.argumentArray != null) {
+ throw new IllegalStateException("argArray has been already set");
+ }
+ this.argumentArray = argArray;
+ }
+
+ public Object[] getArgumentArray() {
+ return this.argumentArray;
+ }
+
+ public Level getLevel() {
+ return level;
+ }
+
+ public String getThreadName() {
+ if (threadName == null) {
+ threadName = (Thread.currentThread()).getName();
+ }
+ return threadName;
+ }
+
+ /**
+ * @param threadName
+ * The threadName to set.
+ * @throws IllegalStateException
+ * If threadName has been already set.
+ */
+ public void setThreadName(String threadName) throws IllegalStateException {
+ if (this.threadName != null) {
+ throw new IllegalStateException("threadName has been already set");
+ }
+ this.threadName = threadName;
+ }
+
+ /**
+ * Returns the throwable information contained within this event. May be
+ * <code>null</code> if there is no such information.
+ */
+ public ThrowableInformation getThrowableInformation() {
+ return throwableInfo;
+ }
+
+ /**
+ * Set this event's throwable information.
+ */
+ public void setThrowableInformation(ThrowableInformation ti) {
+ if (throwableInfo != null) {
+ throw new IllegalStateException(
+ "ThrowableInformation has been already set.");
+ } else {
+ throwableInfo = ti;
+ }
+ }
+
+ /**
+ * This method should be called prior to serializing an event. It should also
+ * be called when using asynchronous logging.
+ */
+ public void prepareForDeferredProcessing() {
+ this.getThreadName();
+ }
+
+ public LoggerRemoteView getLoggerRemoteView() {
+ return loggerRemoteView;
+ }
+
+ public void setLoggerRemoteView(LoggerRemoteView loggerRemoteView) {
+ this.loggerRemoteView = loggerRemoteView;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ if (this.message != null) {
+ throw new IllegalStateException(
+ "The message for this event has been set already.");
+ }
+ this.message = message;
+ }
+
+ public long getTimeStamp() {
+ return timeStamp;
+ }
+
+ public void setTimeStamp(long timeStamp) {
+ this.timeStamp = timeStamp;
+ }
+
+ public void setLevel(Level level) {
+ if (this.level != null) {
+ throw new IllegalStateException(
+ "The level has been already set for this event.");
+ }
+ this.level = level;
+ }
+
+ /**
+ * The time at which this class was loaded into memory, expressed in
+ * millisecond elapsed since the epoch (1.1.1970).
+ *
+ * @return The time as measured when this class was loaded into memory.
+ */
+ public static final long getStartTime() {
+ return startTime;
+ }
+
+ /**
+ * Get the caller information for this logging event. If caller information is
+ * null at the time of its invocation, this method extracts location
+ * information. The collected information is cached for future use.
+ *
+ * <p>
+ * Note that after serialization it is impossible to correctly extract caller
+ * information.
+ * </p>
+ */
+ public CallerData[] getCallerData() {
+ // we rely on the fact that fqnOfLoggerClass does not survive
+ // serialization
+ if (callerDataArray == null && fqnOfLoggerClass != null) {
+ callerDataArray = CallerData.extract(new Throwable(), fqnOfLoggerClass);
+ }
+ return callerDataArray;
+ }
+
+ public void setCallerInformation(CallerData[] callerDataArray) {
+ this.callerDataArray = callerDataArray;
+ }
+
+ public Marker getMarker() {
+ return marker;
+ }
+
+ public void setMarker(Marker marker) {
+ if (this.marker != null) {
+ throw new IllegalStateException(
+ "The marker has been already set for this event.");
+ }
+ this.marker = marker;
+ }
+
+ public String getFormattedMessage() {
+ return formattedMessage;
+ }
+
+ public Map<String, String> getMDCPropertyMap() {
+ return mdcPropertyMap;
+ }
+
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+ out.writeInt(level.levelInt);
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException,
+ ClassNotFoundException {
+ in.defaultReadObject();
+ int levelInt = in.readInt();
+ level = Level.toLevel(levelInt);
+ }
}
Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java Tue Sep 12 09:35:04 2006
@@ -0,0 +1,158 @@
+package ch.qos.logback.classic.html;
+
+import junit.framework.TestCase;
+
+import org.dom4j.Document;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.LoggingEvent;
+import ch.qos.logback.classic.spi.ThrowableInformation;
+import ch.qos.logback.core.appender.ListAppender;
+
+public class HTMLLayoutTest extends TestCase {
+
+ LoggerContext lc;
+ Logger logger;
+ HTMLLayout layout;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ lc = new LoggerContext();
+ lc.setName("default");
+
+ ListAppender appender = new ListAppender();
+ appender.setContext(lc);
+ layout = new HTMLLayout();
+ layout.setContext(lc);
+ layout.setPattern("%level %thread %msg");
+ layout.start();
+ appender.setLayout(layout);
+ logger = lc.getLogger(LoggerContext.ROOT_NAME);
+ logger.addAppender(appender);
+ appender.start();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ lc = null;
+ layout = null;
+ }
+
+ public void testHeader() throws Exception {
+ String header = layout.getHeader();
+ // System.out.println(header);
+ assertTrue(header.indexOf("Level") == 422);
+ assertTrue(header.indexOf("Literal") == 456);
+ assertTrue(header.indexOf("Thread") == 494);
+ assertTrue(header.lastIndexOf("Literal") == 543);
+ assertTrue(header.indexOf("Message") == 139);
+ }
+
+ public void testAppendThrowable() throws Exception {
+ StringBuffer buf = new StringBuffer();
+ String[] strArray = { "test1", "test2" };
+ layout.appendThrowableAsHTML(strArray, buf);
+ // System.out.println(buf.toString());
+ String[] result = buf.toString().split(HTMLLayout.LINE_SEP);
+ assertEquals("test1", result[0]);
+ assertEquals(HTMLLayout.TRACE_PREFIX + "test2", result[1]);
+ }
+
+ public void testDoLayout() throws Exception {
+ LoggingEvent le = createLoggingEvent();
+ String result = layout.doLayout(le);
+ Document doc = parseOutput(result);
+ Element trElement = doc.getRootElement();
+ assertEquals(5, trElement.elements().size());
+ {
+ Element tdElement = (Element) trElement.elements().get(0);
+ assertEquals("DEBUG", tdElement.getText());
+ }
+ {
+ Element tdElement = (Element) trElement.elements().get(1);
+ assertEquals(" ", tdElement.getText());
+ }
+ {
+ Element tdElement = (Element) trElement.elements().get(2);
+ assertEquals("main", tdElement.getText());
+ }
+ {
+ Element tdElement = (Element) trElement.elements().get(3);
+ assertEquals(" ", tdElement.getText());
+ }
+ {
+ Element tdElement = (Element) trElement.elements().get(4);
+ assertEquals("test message", tdElement.getText());
+ }
+ // System.out.println(result);
+ }
+
+ public void testDoLayoutWithException() throws Exception {
+ layout.setPattern("%level %thread %msg %ex");
+ LoggingEvent le = createLoggingEvent();
+ le.setThrowableInformation(new ThrowableInformation(new Exception(
+ "test Exception")));
+ String result = layout.doLayout(le);
+
+ //System.out.println(result);
+
+ Document doc = parseOutput(result);
+ Element trElement = doc.getRootElement();
+ assertEquals(6, trElement.elements().size());
+ {
+ Element tdElement = (Element) trElement.elements().get(0);
+ assertEquals("DEBUG", tdElement.getText());
+ }
+ {
+ Element tdElement = (Element) trElement.elements().get(1);
+ assertEquals(" ", tdElement.getText());
+ }
+ {
+ Element tdElement = (Element) trElement.elements().get(2);
+ assertEquals("main", tdElement.getText());
+ }
+ {
+ Element tdElement = (Element) trElement.elements().get(3);
+ assertEquals(" ", tdElement.getText());
+ }
+ {
+ Element tdElement = (Element) trElement.elements().get(4);
+ assertEquals("test message", tdElement.getText());
+ }
+// {
+// Element trElement2 = (Element) trElement.elements().get(5);
+// Element tdElement = (Element) trElement2.elements().get(0);
+// assertTrue(tdElement.getText().contains(
+// "java.lang.Exception: test Exception"));
+// }
+ }
+
+ public void testLog() {
+ for (int i = 0; i < 2000; i++) {
+ logger.debug("test message");
+ }
+ }
+
+ private LoggingEvent createLoggingEvent() {
+ LoggingEvent le = new LoggingEvent(this.getClass().getName(), logger,
+ Level.DEBUG, "test message", null, null);
+ return le;
+ }
+
+ Document parseOutput(String output) {
+ try {
+ Document document = DocumentHelper.parseText(output);
+ return document;
+ } catch (Exception e) {
+ System.err.println(e);
+ fail();
+ }
+ return null;
+ }
+}
Modified: logback/trunk/pom.xml
==============================================================================
--- logback/trunk/pom.xml (original)
+++ logback/trunk/pom.xml Tue Sep 12 09:35:04 2006
@@ -64,7 +64,11 @@
<artifactId>mail</artifactId>
<version>1.4</version>
</dependency>
-
+ <dependency>
+ <groupId>dom4j</groupId>
+ <artifactId>dom4j</artifactId>
+ <version>1.6.1</version>
+ </dependency>
<!-- Access Module Dependencies -->
<dependency>
More information about the logback-dev
mailing list