avatar

目录
logback邮箱配置更改热更新

在DappSmtpAppender 中 重写了 parseAddress 方法,改成从 配置 中获取邮箱的配置, 支持热更新。

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261

import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.net.SMTPAppender;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.Layout;
import ch.qos.logback.core.boolex.EvaluationException;
import ch.qos.logback.core.helpers.CyclicBuffer;
import ch.qos.logback.core.net.SMTPAppenderBase;
import ch.qos.logback.core.pattern.PatternLayoutBase;
import ch.qos.logback.core.util.ContentTypeUtil;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
*
* 修改mimeMsg.setHeader的时机,避免并发造成的标题覆盖
*
* 继承自SMTPAppender并且覆盖了源有的makeSubjectLayout方法,
* 这是为了实现程序中发送报警邮件的邮件标题修改功能。
*
* @author liuyudut.liu
*/
public class DappSmtpAppender extends SMTPAppender {


private String charsetEncoding = "UTF-8";
static InternetAddress[] EMPTY_IA_ARRAY = new InternetAddress[0];
private static String HOST_NAME;
private static String HOST_ADDRESS;
private static AtomicInteger counter = new AtomicInteger(0);

static {
try {
InetAddress localHost = InetAddress.getLocalHost();
HOST_NAME = localHost.getHostName();
HOST_ADDRESS = localHost.getHostAddress();
} catch (UnknownHostException ignored) {
HOST_NAME = "unknown-host";
HOST_ADDRESS = "unknown-host";
}
}

private String buildTitle(){
return new StringBuilder().append(HOST_NAME)
.append(" [ ").append(HOST_ADDRESS).append(" ]")
.toString();
}

/**
* 邮件主题, 检查是否有丢邮件的情况, 在标题题后面加了计数.
*/
class SubjectLayout extends PatternLayout{
@Override
public String doLayout(ILoggingEvent event) {
return super.doLayout(event) + " " + counter.incrementAndGet();
}
}

@Override
protected Layout makeSubjectLayout(String subjectStr) {
PatternLayout pl = new SubjectLayout();
pl.setContext(getContext());
pl.setPattern(buildTitle());
pl.setPostCompileProcessor(null);
pl.start();
return pl;
}

final static int MAX_DELAY_BETWEEN_STATUS_MESSAGES = 1228800 * CoreConstants.MILLIS_IN_ONE_SECOND;
int delayBetweenStatusMessages = 300 * CoreConstants.MILLIS_IN_ONE_SECOND;
long lastTrackerStatusPrint = 0;
private int errorCount = 0;

private Object lock = new Object();

/**
* 这个方法和父类完全一样, 复写的原因是因为重写了SenderRunnable类
* @param eventObject
*/
protected void append(ILoggingEvent eventObject) {

if (!checkEntryConditions()) {
return;
}

String key = discriminator.getDiscriminatingValue(eventObject);
long now = System.currentTimeMillis();
final CyclicBuffer cb = cbTracker.getOrCreate(key, now);
subAppend(cb, eventObject);
try {
if (eventEvaluator.evaluate(eventObject)) {
// clone the CyclicBuffer before sending out asynchronously
CyclicBuffer cbClone = new CyclicBuffer(cb);
// see http://jira.qos.ch/browse/LBCLASSIC-221
cb.clear();

if (isAsynchronousSending()) {
// perform actual sending asynchronously
SenderRunnable senderRunnable = new SenderRunnable(cbClone, eventObject);
context.getExecutorService().execute(senderRunnable);
} else {
// synchronous sending
sendBuffer(cbClone, eventObject);
}
}
} catch (EvaluationException ex) {
errorCount++;
if (errorCount < CoreConstants.MAX_ERROR_COUNT) {
addError("SMTPAppender's EventEvaluator threw an Exception-", ex);
}
}

// immediately remove the buffer if asked by the user
if (eventMarksEndOfLife(eventObject)) {
cbTracker.endOfLife(key);
}

cbTracker.removeStaleComponents(now);

if (lastTrackerStatusPrint + delayBetweenStatusMessages < now) {
addInfo("SMTPAppender [" + name + "] is tracking [" + cbTracker.getComponentCount() + "] buffers");
lastTrackerStatusPrint = now;
// quadruple 'delay' assuming less than max delay
if (delayBetweenStatusMessages < MAX_DELAY_BETWEEN_STATUS_MESSAGES) {
delayBetweenStatusMessages *= 4;
}
}
}

/**
* 因为邮箱服务端有限制, 所以连续的logger.error最终只有一个会发出邮件
* 猜测可能是服务器一段时间内(经反复实验, 应该小于100ms) 收到的第一封/最后一封邮件
* 又因为是异步发送, 所以就相当于随机了.
*
* 所以重新实现了父类当中的SenderRunnable, run方法中获取了锁(此类单例), 并在每发一封邮件之后, 休眠100ms
*
* 标题挪到同步块中,防止并发造成的标题覆盖
*/
class SenderRunnable implements Runnable {

final CyclicBuffer cyclicBuffer;
final ILoggingEvent e;

SenderRunnable(CyclicBuffer cyclicBuffer, ILoggingEvent e) {
this.cyclicBuffer = cyclicBuffer;
this.e = e;
}

public void run() {
synchronized (lock) {
try {
mimeMsg.setHeader("From", e.getLoggerName() + "@demo.com");
sendBuffer(cyclicBuffer, e);
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException ignored) {
} catch (MessagingException ignored){
}
}
}
}

/**
* 这个方法和父类完全一样, 复写的原因是因为重写了SenderRunnable类
*
*/
protected void sendBuffer(CyclicBuffer cb, ILoggingEvent lastEventObject) {

// 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 header = layout.getFileHeader();
if (header != null) {
sbuf.append(header);
}
String presentationHeader = layout.getPresentationHeader();
if (presentationHeader != null) {
sbuf.append(presentationHeader);
}
fillBuffer(cb, sbuf);
String presentationFooter = layout.getPresentationFooter();
if (presentationFooter != null) {
sbuf.append(presentationFooter);
}
String footer = layout.getFileFooter();
if (footer != null) {
sbuf.append(footer);
}

String subjectStr = "Undefined subject";
if (subjectLayout != null) {
subjectStr = subjectLayout.doLayout(lastEventObject);

// The subject must not contain new-line characters, which cause
// an SMTP error (LOGBACK-865). Truncate the string at the first
// new-line character.
int newLinePos = (subjectStr != null) ? subjectStr.indexOf('\n') : -1;
if (newLinePos > -1) {
subjectStr = subjectStr.substring(0, newLinePos);
}
}
mimeMsg.setSubject(subjectStr, charsetEncoding);

List destinationAddresses = parseAddress(lastEventObject);
if (destinationAddresses.isEmpty()) {
addInfo("Empty destination address. Aborting email transmission");
return;
}

InternetAddress[] toAddressArray = destinationAddresses.toArray(EMPTY_IA_ARRAY);
mimeMsg.setRecipients(Message.RecipientType.TO, toAddressArray);

String contentType = layout.getContentType();

if (ContentTypeUtil.isTextual(contentType)) {
part.setText(sbuf.toString(), charsetEncoding, ContentTypeUtil
.getSubType(contentType));
} else {
part.setContent(sbuf.toString(), layout.getContentType());
}

Multipart mp = new MimeMultipart();
mp.addBodyPart(part);
mimeMsg.setContent(mp);

mimeMsg.setSentDate(new Date());
addInfo("About to send out SMTP message \"" + subjectStr + "\" to " + Arrays.toString(toAddressArray));
Transport.send(mimeMsg);
} catch (Exception e) {
addError("Error occurred while sending e-mail notification.", e);
}
}

private List parseAddress(ILoggingEvent event) {

List iaList = new ArrayList();
iaList.addAll( MailAddConfig.getInternetAddress() ) ;
return iaList;

}

}

对应的分布式配置中心的 类:MailAddConfig, 就可以动态获取

修改logback的配置xml ,新增 自定义的 appender

Code
1
2
3
4
5

xxxx.xxxx.xxxx.xxxx

xxxx.xxxx@xxxx.com

文章作者: 美式不加糖
文章链接: http://yoursite.com/2020/02/04/logback%E9%82%AE%E7%AE%B1%E9%85%8D%E7%BD%AE%E6%9B%B4%E6%94%B9%E7%83%AD%E6%9B%B4%E6%96%B0/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 湖畔小屋
打赏
  • 微信
    微信
  • 支付寶
    支付寶