Hi All,
I've just completed a patch for SMTPAppender which uses NDC to filter
the messages in the generated email so that only buffered messages which
are "related" to the triggering event are included in the email.
The app I need this feature for processes multiple message types
concurrently in different threads, and if an error for message X occurs
I want the resulting email to exclude debug/info/warn/etc messages about
unrelated messages being processed at the same time, and about messages
previously processed by the same thread which were not followed by a
triggering event. By using a modified SMTPAppender, and setting the NDC
to the "message id" when starting processing of a specific message, I
get the desired effect.
Note that this is not the same as filtering, which decides which
messages go into the buffer. [Potentially, this could be implemented by
invoking a "post-trigger" filter object rather than hard-coding the
behaviour into the appender as shown below].
Is there any interest in including this feature in log4j?
The only difference from the standard log4j SMTPAppender is in the
sendBuffer method. I think the javadoc for this method describes the
intent.
I have also modified CyclicBuffer to allow deletion of elements other
than the oldest, and allow -ve indexes to the get method which return
objects relative to the *newest* (rather than oldest) message. Removing
elements from the CyclicBuffer is optional, but makes more effective use
of it. I haven't included this patch, but am happy to post it on
request.
I think it is easiest to understand if I include the whole method; a
diff is also attached.
/**
Send the contents of the cyclic buffer as an e-mail message.
<p>
This variant of the original SMTPAppender only sends events which
have the same NDC (or an ancestor NDC, or no NDC) as the triggering
event. This ensures that issues relating to different contexts are
not mixed together in the same mail.
<p>
Example:
<pre>
[no ndc] INFO: starting
[ndc=src1] INFO: handling a msg from src1
[ndc=src2] INFO: handling a msg from src2
[ndc=src1] ERROR: bad FOO
[ndc=src2] ERROR: bad BAR
</pre>
The first generated message will have:
<pre>
INFO: starting
INFO: handling a msg from src1
ERROR: bad FOO
</pre>
and the second generated message will have:
<pre>
INFO: starting
INFO: handling a msg from src2
ERROR: bad BAR
</pre>
<p>
The price of this is a little extra memory usage and cpu time.
Without
this feature, the buffer can be completely flushed every time a
trigger
event is seen. With this feature, only messages with an NDC exactly
matching the trigger event can be deleted.
*/
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();
LoggingEvent triggerEvent = cb.get(-1);
String triggerNDC = triggerEvent.getNDC();
if (triggerNDC == null) {
triggerNDC = "";
}
for (int i = 0; i < len; i++) {
//sbuf.append(MimeUtility.encodeText(layout.format(cb.get())));
LoggingEvent event = cb.get(i);
String ndc = event.getNDC();
if (ndc == null) {
ndc = "";
}
if (triggerNDC.startsWith(ndc)) {
if (triggerNDC.equals(ndc)) {
cb.remove(i);
}
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]);
sbuf.append(Layout.LINE_SEP);
}
}
}
}
}
cb.pack();
t = layout.getFooter();
if (t != null) {
sbuf.append(t);
}
part.setContent(sbuf.toString(), layout.getContentType());
Multipart mp = new MimeMultipart();
mp.addBodyPart(part);
msg.setContent(mp);
msg.setSentDate(new Date());
Transport.send(msg);
} catch (Exception e) {
LogLog.error("Error occured while sending e-mail notification.",
e);
}
}
Regards,
Simon
--- SMTPAppender.java 2004-02-28 05:47:32.000000000 +1300
+++ /tmp/SMTPAppender.java 2004-03-26 15:52:11.000000000 +1200
@@ -12,14 +12,18 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
+ *
+ * This file has been modified by Simon Kitching, The Electronic Commerce
+ * Network Ltd. This modified version puts into the email only messages from
+ * the buffer whose NDC is the same as, or an ancestor of, the triggering event.
*/
-package org.apache.log4j.net;
+package nz.co.ecnetwork.connector.logging;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
-import org.apache.log4j.helpers.CyclicBuffer;
+//import org.apache.log4j.helpers.CyclicBuffer;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.spi.ErrorCode;
@@ -208,6 +212,37 @@
/**
Send the contents of the cyclic buffer as an e-mail message.
+ <p>
+ This variant of the original SMTPAppender only sends events which
+ have the same NDC (or an ancestor NDC, or no NDC) as the triggering
+ event. This ensures that issues relating to different contexts are
+ not mixed together in the same mail.
+ <p>
+ Example:
+ <pre>
+ [no ndc] INFO: starting
+ [ndc=src1] INFO: handling a msg from src1
+ [ndc=src2] INFO: handling a msg from src2
+ [ndc=src1] ERROR: bad FOO
+ [ndc=src2] ERROR: bad BAR
+ </pre>
+ The first generated message will have:
+ <pre>
+ INFO: starting
+ INFO: handling a msg from src1
+ ERROR: bad FOO
+ </pre>
+ and the second generated message will have:
+ <pre>
+ INFO: starting
+ INFO: handling a msg from src2
+ ERROR: bad BAR
+ </pre>
+ <p>
+ The price of this is a little extra memory usage and cpu time. Without
+ this feature, the buffer can be completely flushed every time a trigger
+ event is seen. With this feature, only messages with an NDC exactly
+ matching the trigger event can be deleted.
*/
protected void sendBuffer() {
// Note: this code already owns the monitor for this
@@ -224,22 +259,40 @@
int len = cb.length();
+ LoggingEvent triggerEvent = cb.get(-1);
+ String triggerNDC = triggerEvent.getNDC();
+ if (triggerNDC == null) {
+ triggerNDC = "";
+ }
+
for (int i = 0; i < len; i++) {
//sbuf.append(MimeUtility.encodeText(layout.format(cb.get())));
- LoggingEvent event = cb.get();
- sbuf.append(layout.format(event));
+ LoggingEvent event = cb.get(i);
+ String ndc = event.getNDC();
+ if (ndc == null) {
+ ndc = "";
+ }
+
+ if (triggerNDC.startsWith(ndc)) {
+ if (triggerNDC.equals(ndc)) {
+ cb.remove(i);
+ }
- if (layout.ignoresThrowable()) {
- String[] s = event.getThrowableStrRep();
+ sbuf.append(layout.format(event));
- if (s != null) {
- for (int j = 0; j < s.length; j++) {
- sbuf.append(s[j]);
- sbuf.append(Layout.LINE_SEP);
+ if (layout.ignoresThrowable()) {
+ String[] s = event.getThrowableStrRep();
+
+ if (s != null) {
+ for (int j = 0; j < s.length; j++) {
+ sbuf.append(s[j]);
+ sbuf.append(Layout.LINE_SEP);
+ }
}
}
}
}
+ cb.pack();
t = layout.getFooter();
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]