com.taotao.boot.monitor.warn.WarnProvider Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2020-2030, Shuigedeng ([email protected] & https://blog.taotaocloud.top/).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* 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.
*/
package com.taotao.boot.monitor.warn;
import com.taotao.boot.common.constant.CommonConstant;
import com.taotao.boot.common.constant.StarterName;
import com.taotao.boot.common.utils.common.PropertyUtils;
import com.taotao.boot.common.utils.lang.StringUtils;
import com.taotao.boot.common.utils.log.LogUtils;
import com.taotao.boot.common.utils.servlet.RequestUtils;
import com.taotao.boot.monitor.Monitor;
import com.taotao.boot.monitor.enums.WarnTypeEnum;
import com.taotao.boot.monitor.model.Message;
import com.taotao.boot.monitor.properties.WarnProperties;
import com.taotao.boot.monitor.utils.ExceptionUtils;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
* WarnProvider
*
* @author shuigedeng
* @version 2021.9
* @since 2021-09-09 11:04:53
*/
public class WarnProvider extends AbstractWarn implements AutoCloseable, ApplicationRunner {
private boolean isClose;
private final Monitor monitorThreadPool;
private final WarnProperties warnProperties;
private final DuplicateFilter duplicateFilter;
private final Object lock = new Object();
private final List warns = new ArrayList<>();
private final ConcurrentLinkedDeque messages = new ConcurrentLinkedDeque<>();
private final AtomicInteger atomicInteger = new AtomicInteger(0);
private final AtomicBoolean atomicChannel = new AtomicBoolean(false);
public WarnProvider(WarnProperties warnProperties, Monitor monitorThreadPool) {
this.warnProperties = warnProperties;
this.monitorThreadPool = monitorThreadPool;
this.duplicateFilter = new DuplicateFilter(warnProperties);
this.isClose = false;
registerWarn();
this.monitorThreadPool.monitorSubmit("系统任务: WarnProvider 实时报警任务", () -> {
while (!this.monitorThreadPool.monitorIsShutdown() && !isClose) {
try {
notifyRunning();
} catch (Exception exp) {
LogUtils.warn(StarterName.MONITOR_STARTER, "WarnProvider 消息循环异常");
}
try {
Thread.sleep(warnProperties.getTimeSpan() * 1000L);
} catch (Exception e) {
LogUtils.error(e);
}
}
});
}
/**
* registerWarn
*
*
* @since 2021-09-10 16:18:47
*/
public void registerWarn() {
if (warnProperties.isDingDingWarnEnabled()) {
warns.add(new DingdingWarn());
}
if (warnProperties.getEmailWarnEnabled()) {
warns.add(new MailWarn());
}
if (warnProperties.getSmsWarnEnabled()) {
warns.add(new SmsWarn());
}
warns.add(new LoggerWarn());
}
/**
* clearWarn
*
*
* @since 2021-09-10 16:19:01
*/
public void clearWarn() {
warns.clear();
}
/**
* notifyRunning
*
*
* @since 2021-09-10 16:19:04
*/
private void notifyRunning() {
Message msg = new Message();
msg.setWarnType(WarnTypeEnum.WARN);
List msgs = getAllMessage();
int msgCount = atomicInteger.getAndSet(0);
if (msgCount > 0) {
StringBuilder content = new StringBuilder();
content.append(
String.format("最新报警累计:%s条,详情请查看日志系统,最后%s条报警内容如下:\n", msgCount, warnProperties.getCacheCount()));
msgs.forEach(c -> {
if (c.getWarnType().getLevel() > (msg.getWarnType()).getLevel()) {
msg.setWarnType(c.getWarnType());
}
content.append(String.format(
"[%s][%s]内容%s\n", c.getWarnType().getDescription(), c.getTitle(), c.getContent()));
});
msg.setTitle(String.format("收到%s条报警", msgCount));
msg.setContent(content.toString());
notifyNow(msg);
}
}
@Override
public void notify(Message message) {
addMessage(message);
}
/**
* notify
*
* @param warnType warnType
* @param title title
* @param content content
*
* @since 2021-09-10 16:19:41
*/
public void notify(String warnType, String title, String content) {
Message message = new Message(WarnTypeEnum.valueOf(warnType), title, content);
addMessage(message);
}
/**
* addMessage
*
* @param msg msg
*
* @since 2021-09-10 16:19:44
*/
private void addMessage(Message msg) {
atomicInteger.getAndIncrement();
// 加锁
synchronized (lock) {
messages.add(msg);
// 清理多余
if (messages.size() > warnProperties.getCacheCount()) {
int cacheCount = warnProperties.getCacheCount();
for (int i = 0; i < messages.size() - cacheCount; i++) {
if (!messages.isEmpty()) {
messages.removeFirst();
}
}
}
}
}
/**
* getAllMessage
*
* @return {@link List }
*
* @since 2021-09-10 16:20:17
*/
private List getAllMessage() {
List msgs = new ArrayList<>(messages.size());
synchronized (lock) {
msgs.addAll(messages);
messages.clear();
}
return msgs;
}
/**
* notifyNow
*
* @param message message
*
* @since 2021-09-10 16:20:33
*/
public void notifyNow(Message message) {
notifyMessage0(message);
}
/**
* notifyNow
*
* @param warnType warnType
* @param title title
* @param content content
*
* @since 2021-09-10 16:20:38
*/
public void notifyNow(String warnType, String title, String content) {
Message message = new Message(WarnTypeEnum.valueOf(warnType), title, content);
notifyMessage0(message);
}
/**
* 方法私有化,避免重载方法循环调用
*
* @param message message
*
* @since 2021-09-10 16:20:48
*/
private void notifyMessage0(Message message) {
if (!duplicateFilter.ifDuplicate(message.getContent()) && atomicChannel.get()) {
if (WarnTypeEnum.ERROR == message.getWarnType()) {
ExceptionUtils.reportException(message);
}
for (AbstractWarn warn : warns) {
message.setTitle(String.format(
"[%s][%s][%s][%s]%s",
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()),
RequestUtils.getIpAddress(),
PropertyUtils.getProperty(CommonConstant.SPRING_APP_NAME_KEY),
PropertyUtils.getProperty(CommonConstant.SPRING_APP_NAME_KEY),
StringUtils.nullToEmpty(message.getTitle())));
warn.notify(message);
}
}
}
@Override
public void close() {
isClose = true;
}
@Override
public void run(ApplicationArguments args) {
atomicChannel.getAndSet(true);
LogUtils.info(StarterName.MONITOR_STARTER, "开启消息通道");
}
/**
* 简单重复过滤算法 去除数值并hash
*
*
* @version 2021.9
* @since 2021-09-10 16:21:09
*/
private static class DuplicateFilter {
private WarnProperties warnProperties;
private int cacheMax = 100;
private volatile List cacheTag = new ArrayList<>(cacheMax + 5);
private long lastClearTime = System.currentTimeMillis();
public DuplicateFilter(WarnProperties warnProperties) {
this.warnProperties = warnProperties;
}
/**
* ifDuplicate
*
* @param message message
* @return boolean
*
* @since 2021-09-10 16:21:52
*/
public boolean ifDuplicate(String message) {
int hash = StringUtils.nullToEmpty(message).replaceAll("\\d+", "").hashCode();
// 超过1分钟清理
if (System.currentTimeMillis() - lastClearTime
> TimeUnit.MINUTES.toMillis(this.warnProperties.getDuplicateTimeSpan())) {
cacheTag.clear();
lastClearTime = System.currentTimeMillis();
}
// 过长清理
if (cacheTag.size() >= cacheMax) {
cacheTag.clear();
}
if (!cacheTag.contains(hash)) {
cacheTag.add(hash);
return false;
}
return true;
}
}
}