1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.log4j.net;
19
20 import org.apache.log4j.AppenderSkeleton;
21 import org.apache.log4j.Layout;
22 import org.apache.log4j.Level;
23 import org.apache.log4j.helpers.CyclicBuffer;
24 import org.apache.log4j.helpers.LogLog;
25 import org.apache.log4j.helpers.OptionConverter;
26 import org.apache.log4j.spi.ErrorCode;
27 import org.apache.log4j.spi.LoggingEvent;
28 import org.apache.log4j.spi.OptionHandler;
29 import org.apache.log4j.spi.TriggeringEventEvaluator;
30 import org.apache.log4j.xml.UnrecognizedElementHandler;
31 import org.w3c.dom.Element;
32
33 import javax.mail.Authenticator;
34 import javax.mail.Message;
35 import javax.mail.MessagingException;
36 import javax.mail.Multipart;
37 import javax.mail.PasswordAuthentication;
38 import javax.mail.Session;
39 import javax.mail.Transport;
40 import javax.mail.internet.AddressException;
41 import javax.mail.internet.InternetAddress;
42 import javax.mail.internet.InternetHeaders;
43 import javax.mail.internet.MimeBodyPart;
44 import javax.mail.internet.MimeMessage;
45 import javax.mail.internet.MimeMultipart;
46 import javax.mail.internet.MimeUtility;
47 import java.io.ByteArrayOutputStream;
48 import java.io.OutputStreamWriter;
49 import java.io.UnsupportedEncodingException;
50 import java.io.Writer;
51 import java.util.Date;
52 import java.util.Properties;
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 public class SMTPAppender extends AppenderSkeleton
80 implements UnrecognizedElementHandler {
81 private String to;
82
83
84
85 private String cc;
86
87
88
89 private String bcc;
90 private String from;
91
92
93
94 private String replyTo;
95 private String subject;
96 private String smtpHost;
97 private String smtpUsername;
98 private String smtpPassword;
99 private String smtpProtocol;
100 private int smtpPort = -1;
101 private boolean smtpDebug = false;
102 private int bufferSize = 512;
103 private boolean locationInfo = false;
104 private boolean sendOnClose = false;
105
106 protected CyclicBuffer cb = new CyclicBuffer(bufferSize);
107 protected Message msg;
108
109 protected TriggeringEventEvaluator evaluator;
110
111
112
113
114
115
116
117 public
118 SMTPAppender() {
119 this(new DefaultEvaluator());
120 }
121
122
123
124
125
126 public
127 SMTPAppender(TriggeringEventEvaluator evaluator) {
128 this.evaluator = evaluator;
129 }
130
131
132
133
134
135 public
136 void activateOptions() {
137 Session session = createSession();
138 msg = new MimeMessage(session);
139
140 try {
141 addressMessage(msg);
142 if(subject != null) {
143 try {
144 msg.setSubject(MimeUtility.encodeText(subject, "UTF-8", null));
145 } catch(UnsupportedEncodingException ex) {
146 LogLog.error("Unable to encode SMTP subject", ex);
147 }
148 }
149 } catch(MessagingException e) {
150 LogLog.error("Could not activate SMTPAppender options.", e );
151 }
152
153 if (evaluator instanceof OptionHandler) {
154 ((OptionHandler) evaluator).activateOptions();
155 }
156 }
157
158
159
160
161
162
163
164 protected void addressMessage(final Message msg) throws MessagingException {
165 if (from != null) {
166 msg.setFrom(getAddress(from));
167 } else {
168 msg.setFrom();
169 }
170
171
172 if (replyTo != null && replyTo.length() > 0) {
173 msg.setReplyTo(parseAddress(replyTo));
174 }
175
176 if (to != null && to.length() > 0) {
177 msg.setRecipients(Message.RecipientType.TO, parseAddress(to));
178 }
179
180
181 if (cc != null && cc.length() > 0) {
182 msg.setRecipients(Message.RecipientType.CC, parseAddress(cc));
183 }
184
185
186 if (bcc != null && bcc.length() > 0) {
187 msg.setRecipients(Message.RecipientType.BCC, parseAddress(bcc));
188 }
189 }
190
191
192
193
194
195
196 protected Session createSession() {
197 Properties props = null;
198 try {
199 props = new Properties (System.getProperties());
200 } catch(SecurityException ex) {
201 props = new Properties();
202 }
203
204 String prefix = "mail.smtp";
205 if (smtpProtocol != null) {
206 props.put("mail.transport.protocol", smtpProtocol);
207 prefix = "mail." + smtpProtocol;
208 }
209 if (smtpHost != null) {
210 props.put(prefix + ".host", smtpHost);
211 }
212 if (smtpPort > 0) {
213 props.put(prefix + ".port", String.valueOf(smtpPort));
214 }
215
216 Authenticator auth = null;
217 if(smtpPassword != null && smtpUsername != null) {
218 props.put(prefix + ".auth", "true");
219 auth = new Authenticator() {
220 protected PasswordAuthentication getPasswordAuthentication() {
221 return new PasswordAuthentication(smtpUsername, smtpPassword);
222 }
223 };
224 }
225 Session session = Session.getInstance(props, auth);
226 if (smtpProtocol != null) {
227 session.setProtocolForAddress("rfc822", smtpProtocol);
228 }
229 if (smtpDebug) {
230 session.setDebug(smtpDebug);
231 }
232 return session;
233 }
234
235
236
237
238
239 public
240 void append(LoggingEvent event) {
241
242 if(!checkEntryConditions()) {
243 return;
244 }
245
246 event.getThreadName();
247 event.getNDC();
248 event.getMDCCopy();
249 if(locationInfo) {
250 event.getLocationInformation();
251 }
252 event.getRenderedMessage();
253 event.getThrowableStrRep();
254 cb.add(event);
255 if(evaluator.isTriggeringEvent(event)) {
256 sendBuffer();
257 }
258 }
259
260
261
262
263
264
265
266 protected
267 boolean checkEntryConditions() {
268 if(this.msg == null) {
269 errorHandler.error("Message object not configured.");
270 return false;
271 }
272
273 if(this.evaluator == null) {
274 errorHandler.error("No TriggeringEventEvaluator is set for appender ["+
275 name+"].");
276 return false;
277 }
278
279
280 if(this.layout == null) {
281 errorHandler.error("No layout set for appender named ["+name+"].");
282 return false;
283 }
284 return true;
285 }
286
287
288 synchronized
289 public
290 void close() {
291 this.closed = true;
292 if (sendOnClose && cb.length() > 0) {
293 sendBuffer();
294 }
295 }
296
297 InternetAddress getAddress(String addressStr) {
298 try {
299 return new InternetAddress(addressStr);
300 } catch(AddressException e) {
301 errorHandler.error("Could not parse address ["+addressStr+"].", e,
302 ErrorCode.ADDRESS_PARSE_FAILURE);
303 return null;
304 }
305 }
306
307 InternetAddress[] parseAddress(String addressStr) {
308 try {
309 return InternetAddress.parse(addressStr, true);
310 } catch(AddressException e) {
311 errorHandler.error("Could not parse address ["+addressStr+"].", e,
312 ErrorCode.ADDRESS_PARSE_FAILURE);
313 return null;
314 }
315 }
316
317
318
319
320 public
321 String getTo() {
322 return to;
323 }
324
325
326
327
328
329 public
330 boolean requiresLayout() {
331 return true;
332 }
333
334
335
336
337
338 protected String formatBody() {
339
340
341
342
343 StringBuffer sbuf = new StringBuffer();
344 String t = layout.getHeader();
345 if(t != null)
346 sbuf.append(t);
347 int len = cb.length();
348 for(int i = 0; i < len; i++) {
349
350 LoggingEvent event = cb.get();
351 sbuf.append(layout.format(event));
352 if(layout.ignoresThrowable()) {
353 String[] s = event.getThrowableStrRep();
354 if (s != null) {
355 for(int j = 0; j < s.length; j++) {
356 sbuf.append(s[j]);
357 sbuf.append(Layout.LINE_SEP);
358 }
359 }
360 }
361 }
362 t = layout.getFooter();
363 if(t != null) {
364 sbuf.append(t);
365 }
366
367 return sbuf.toString();
368 }
369
370
371
372
373 protected
374 void sendBuffer() {
375
376 try {
377 String s = formatBody();
378 boolean allAscii = true;
379 for(int i = 0; i < s.length() && allAscii; i++) {
380 allAscii = s.charAt(i) <= 0x7F;
381 }
382 MimeBodyPart part;
383 if (allAscii) {
384 part = new MimeBodyPart();
385 part.setContent(s, layout.getContentType());
386 } else {
387 try {
388 ByteArrayOutputStream os = new ByteArrayOutputStream();
389 Writer writer = new OutputStreamWriter(
390 MimeUtility.encode(os, "quoted-printable"), "UTF-8");
391 writer.write(s);
392 writer.close();
393 InternetHeaders headers = new InternetHeaders();
394 headers.setHeader("Content-Type", layout.getContentType() + "; charset=UTF-8");
395 headers.setHeader("Content-Transfer-Encoding", "quoted-printable");
396 part = new MimeBodyPart(headers, os.toByteArray());
397 } catch(Exception ex) {
398 StringBuffer sbuf = new StringBuffer(s);
399 for (int i = 0; i < sbuf.length(); i++) {
400 if (sbuf.charAt(i) >= 0x80) {
401 sbuf.setCharAt(i, '?');
402 }
403 }
404 part = new MimeBodyPart();
405 part.setContent(sbuf.toString(), layout.getContentType());
406 }
407 }
408
409
410
411 Multipart mp = new MimeMultipart();
412 mp.addBodyPart(part);
413 msg.setContent(mp);
414
415 msg.setSentDate(new Date());
416 Transport.send(msg);
417 } catch(MessagingException e) {
418 LogLog.error("Error occured while sending e-mail notification.", e);
419 } catch(RuntimeException e) {
420 LogLog.error("Error occured while sending e-mail notification.", e);
421 }
422 }
423
424
425
426
427
428
429 public
430 String getEvaluatorClass() {
431 return evaluator == null ? null : evaluator.getClass().getName();
432 }
433
434
435
436
437 public
438 String getFrom() {
439 return from;
440 }
441
442
443
444
445
446
447 public
448 String getReplyTo() {
449 return replyTo;
450 }
451
452
453
454
455 public
456 String getSubject() {
457 return subject;
458 }
459
460
461
462
463
464 public
465 void setFrom(String from) {
466 this.from = from;
467 }
468
469
470
471
472
473
474 public
475 void setReplyTo(final String addresses) {
476 this.replyTo = addresses;
477 }
478
479
480
481
482
483
484 public
485 void setSubject(String subject) {
486 this.subject = subject;
487 }
488
489
490
491
492
493
494
495
496
497 public
498 void setBufferSize(int bufferSize) {
499 this.bufferSize = bufferSize;
500 cb.resize(bufferSize);
501 }
502
503
504
505
506
507 public
508 void setSMTPHost(String smtpHost) {
509 this.smtpHost = smtpHost;
510 }
511
512
513
514
515 public
516 String getSMTPHost() {
517 return smtpHost;
518 }
519
520
521
522
523
524 public
525 void setTo(String to) {
526 this.to = to;
527 }
528
529
530
531
532
533
534 public
535 int getBufferSize() {
536 return bufferSize;
537 }
538
539
540
541
542
543
544
545
546 public
547 void setEvaluatorClass(String value) {
548 evaluator = (TriggeringEventEvaluator)
549 OptionConverter.instantiateByClassName(value,
550 TriggeringEventEvaluator.class,
551 evaluator);
552 }
553
554
555
556
557
558
559
560
561
562
563
564
565
566 public
567 void setLocationInfo(boolean locationInfo) {
568 this.locationInfo = locationInfo;
569 }
570
571
572
573
574 public
575 boolean getLocationInfo() {
576 return locationInfo;
577 }
578
579
580
581
582
583
584 public void setCc(final String addresses) {
585 this.cc = addresses;
586 }
587
588
589
590
591
592
593 public String getCc() {
594 return cc;
595 }
596
597
598
599
600
601
602 public void setBcc(final String addresses) {
603 this.bcc = addresses;
604 }
605
606
607
608
609
610
611 public String getBcc() {
612 return bcc;
613 }
614
615
616
617
618
619
620
621 public void setSMTPPassword(final String password) {
622 this.smtpPassword = password;
623 }
624
625
626
627
628
629
630
631 public void setSMTPUsername(final String username) {
632 this.smtpUsername = username;
633 }
634
635
636
637
638
639
640
641
642 public void setSMTPDebug(final boolean debug) {
643 this.smtpDebug = debug;
644 }
645
646
647
648
649
650
651 public String getSMTPPassword() {
652 return smtpPassword;
653 }
654
655
656
657
658
659
660 public String getSMTPUsername() {
661 return smtpUsername;
662 }
663
664
665
666
667
668
669 public boolean getSMTPDebug() {
670 return smtpDebug;
671 }
672
673
674
675
676
677
678 public final void setEvaluator(final TriggeringEventEvaluator trigger) {
679 if (trigger == null) {
680 throw new NullPointerException("trigger");
681 }
682 this.evaluator = trigger;
683 }
684
685
686
687
688
689
690 public final TriggeringEventEvaluator getEvaluator() {
691 return evaluator;
692 }
693
694
695
696
697 public boolean parseUnrecognizedElement(final Element element,
698 final Properties props) throws Exception {
699 if ("triggeringPolicy".equals(element.getNodeName())) {
700 Object triggerPolicy =
701 org.apache.log4j.xml.DOMConfigurator.parseElement(
702 element, props, TriggeringEventEvaluator.class);
703 if (triggerPolicy instanceof TriggeringEventEvaluator) {
704 setEvaluator((TriggeringEventEvaluator) triggerPolicy);
705 }
706 return true;
707 }
708
709 return false;
710 }
711
712
713
714
715
716
717
718
719 public final String getSMTPProtocol() {
720 return smtpProtocol;
721 }
722
723
724
725
726
727
728
729
730 public final void setSMTPProtocol(final String val) {
731 smtpProtocol = val;
732 }
733
734
735
736
737
738
739
740 public final int getSMTPPort() {
741 return smtpPort;
742 }
743
744
745
746
747
748
749
750 public final void setSMTPPort(final int val) {
751 smtpPort = val;
752 }
753
754
755
756
757
758
759
760 public final boolean getSendOnClose() {
761 return sendOnClose;
762 }
763
764
765
766
767
768
769
770 public final void setSendOnClose(final boolean val) {
771 sendOnClose = val;
772 }
773
774 }
775
776 class DefaultEvaluator implements TriggeringEventEvaluator {
777
778
779
780
781
782
783 public
784 boolean isTriggeringEvent(LoggingEvent event) {
785 return event.getLevel().isGreaterOrEqual(Level.ERROR);
786 }
787 }