|
@@ -4,25 +4,24 @@ import java.util.Map;
|
|
|
import java.util.Objects;
|
|
|
import java.util.concurrent.locks.Lock;
|
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
|
+import javax.mail.Session;
|
|
|
|
|
|
-import ch.qos.logback.classic.Level;
|
|
|
import ch.qos.logback.classic.filter.ThresholdFilter;
|
|
|
import ch.qos.logback.classic.spi.ILoggingEvent;
|
|
|
import ch.qos.logback.classic.spi.IThrowableProxy;
|
|
|
import ch.qos.logback.core.AppenderBase;
|
|
|
import ch.qos.logback.core.Layout;
|
|
|
import ch.qos.logback.core.spi.FilterReply;
|
|
|
-import com.chelvc.framework.base.context.ApplicationContextHolder;
|
|
|
import com.chelvc.framework.base.util.SpringUtils;
|
|
|
import com.chelvc.framework.common.util.StringUtils;
|
|
|
+import com.chelvc.framework.email.config.EmailProperties;
|
|
|
+import com.chelvc.framework.email.context.EmailContextHolder;
|
|
|
import com.google.common.collect.Maps;
|
|
|
-import lombok.AccessLevel;
|
|
|
+import lombok.Data;
|
|
|
+import lombok.EqualsAndHashCode;
|
|
|
import lombok.Setter;
|
|
|
+import lombok.ToString;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
-import org.springframework.beans.BeansException;
|
|
|
-import org.springframework.context.ApplicationContext;
|
|
|
-import org.springframework.context.ApplicationContextAware;
|
|
|
-import org.springframework.stereotype.Component;
|
|
|
|
|
|
/**
|
|
|
* Logback日志邮件发送器
|
|
@@ -31,23 +30,19 @@ import org.springframework.stereotype.Component;
|
|
|
* @date 2024/5/20
|
|
|
*/
|
|
|
@Slf4j
|
|
|
-@Setter
|
|
|
-@Component
|
|
|
-public class LogbackEmailAppender extends AppenderBase<ILoggingEvent> implements ApplicationContextAware {
|
|
|
- /**
|
|
|
- * 邮件处理器实例
|
|
|
- */
|
|
|
- private static EmailHandler EMAIL_HANDLER;
|
|
|
+public class LogbackEmailAppender extends AppenderBase<ILoggingEvent> {
|
|
|
+ @Setter
|
|
|
+ private EmailConfig email;
|
|
|
|
|
|
- private String to;
|
|
|
- private String subject;
|
|
|
- private long idle = 60000;
|
|
|
+ @Setter
|
|
|
private Layout<ILoggingEvent> layout;
|
|
|
|
|
|
+ private final long idle = 60000;
|
|
|
private final Lock lock = new ReentrantLock();
|
|
|
+ private final String name = this.getClass().getName();
|
|
|
private final Map<Integer, Long> caches = Maps.newConcurrentMap();
|
|
|
|
|
|
- @Setter(AccessLevel.NONE)
|
|
|
+ private volatile EmailHandler handler;
|
|
|
private volatile long cleaning = System.currentTimeMillis();
|
|
|
|
|
|
/**
|
|
@@ -55,8 +50,8 @@ public class LogbackEmailAppender extends AppenderBase<ILoggingEvent> implements
|
|
|
*
|
|
|
* @return true/false
|
|
|
*/
|
|
|
- protected boolean isReady() {
|
|
|
- return this.layout != null && EMAIL_HANDLER != null && StringUtils.notEmpty(this.to);
|
|
|
+ private boolean isReady() {
|
|
|
+ return this.email != null && this.layout != null;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -64,8 +59,16 @@ public class LogbackEmailAppender extends AppenderBase<ILoggingEvent> implements
|
|
|
*
|
|
|
* @return 邮件处理器
|
|
|
*/
|
|
|
- protected EmailHandler getEmailHandler() {
|
|
|
- return Objects.requireNonNull(EMAIL_HANDLER);
|
|
|
+ private EmailHandler getEmailHandler() {
|
|
|
+ if (this.handler == null) {
|
|
|
+ synchronized (this) {
|
|
|
+ if (this.handler == null) {
|
|
|
+ Session session = EmailContextHolder.session(this.email);
|
|
|
+ this.handler = new DefaultEmailHandler(session, this.email);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return this.handler;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -74,7 +77,7 @@ public class LogbackEmailAppender extends AppenderBase<ILoggingEvent> implements
|
|
|
* @param event 日志事件对象实例
|
|
|
* @return hash值
|
|
|
*/
|
|
|
- protected int hash(ILoggingEvent event) {
|
|
|
+ private int hash(ILoggingEvent event) {
|
|
|
IThrowableProxy throwable = event.getThrowableProxy();
|
|
|
int hash = throwable == null ? 0 : Objects.hash((Object[]) throwable.getStackTraceElementProxyArray());
|
|
|
return 31 * Math.max(hash, 1) + Objects.hashCode(event.getLoggerName());
|
|
@@ -86,10 +89,7 @@ public class LogbackEmailAppender extends AppenderBase<ILoggingEvent> implements
|
|
|
* @param event 日志事件对象实例
|
|
|
* @return true/false
|
|
|
*/
|
|
|
- protected boolean isDuplicated(ILoggingEvent event) {
|
|
|
- if (this.idle < 1) {
|
|
|
- return false;
|
|
|
- }
|
|
|
+ private boolean isDuplicated(ILoggingEvent event) {
|
|
|
int hash = this.hash(event);
|
|
|
long now = System.currentTimeMillis();
|
|
|
Long timestamp = this.caches.put(hash, now);
|
|
@@ -101,23 +101,16 @@ public class LogbackEmailAppender extends AppenderBase<ILoggingEvent> implements
|
|
|
*
|
|
|
* @param event 日志事件对象实例
|
|
|
*/
|
|
|
- protected void send(ILoggingEvent event) {
|
|
|
+ private void send(ILoggingEvent event) {
|
|
|
String type = this.layout.getContentType();
|
|
|
String content = this.layout.doLayout(event);
|
|
|
- Body body = Body.builder().to(this.to).type(type).subject(this.subject).content(content).build();
|
|
|
+ Body body = Body.builder().to(this.email.to).type(type).subject(this.email.subject).content(content).build();
|
|
|
this.getEmailHandler().send(body);
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
|
|
- EMAIL_HANDLER = applicationContext.getBean(EmailHandler.class);
|
|
|
- }
|
|
|
-
|
|
|
@Override
|
|
|
protected void append(ILoggingEvent event) {
|
|
|
- boolean enabled = ApplicationContextHolder.getProperty("logback.email.enabled", boolean.class, true);
|
|
|
- if (!enabled || !this.isReady() || LogbackEmailAppender.class.getName().equals(event.getLoggerName())
|
|
|
- || (event.getLevel() == Level.ERROR && this.isDuplicated(event))) {
|
|
|
+ if (!this.isReady() || this.name.equals(event.getLoggerName()) || this.isDuplicated(event)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -129,21 +122,37 @@ public class LogbackEmailAppender extends AppenderBase<ILoggingEvent> implements
|
|
|
log.warn("Email send failed: {}", e.getMessage());
|
|
|
} finally {
|
|
|
// 清理日志缓存
|
|
|
- if (this.idle > 0) {
|
|
|
- long now = System.currentTimeMillis();
|
|
|
- if (now - this.cleaning > this.idle && this.lock.tryLock()) {
|
|
|
- try {
|
|
|
- this.caches.entrySet().removeIf(entry -> now - entry.getValue() > this.idle);
|
|
|
- this.cleaning = now;
|
|
|
- } finally {
|
|
|
- this.lock.unlock();
|
|
|
- }
|
|
|
+ long now = System.currentTimeMillis();
|
|
|
+ if (now - this.cleaning > this.idle && this.lock.tryLock()) {
|
|
|
+ try {
|
|
|
+ this.caches.entrySet().removeIf(entry -> now - entry.getValue() > this.idle);
|
|
|
+ this.cleaning = now;
|
|
|
+ } finally {
|
|
|
+ this.lock.unlock();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 日志邮件配置
|
|
|
+ */
|
|
|
+ @Data
|
|
|
+ @ToString(callSuper = true)
|
|
|
+ @EqualsAndHashCode(callSuper = true)
|
|
|
+ public static class EmailConfig extends EmailProperties {
|
|
|
+ /**
|
|
|
+ * 收件人
|
|
|
+ */
|
|
|
+ private String to;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 邮件主题
|
|
|
+ */
|
|
|
+ private String subject;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 日志邮件过滤器实现
|
|
|
*/
|