fix(G): 设备控制指令业务异常降噪, 避免定时场景刷爆错误日志
问题:定时场景(SceneCronScheduler, 如 bb 场景)每分钟执行 7 条规则并向涂鸦设备下发控制指令, 而设备归属用户尚未绑定涂鸦账号,每次下发都抛 BusinessException,并以 ERROR + 完整堆栈记录, 单日产生上千条错误日志,掩盖真实故障。 改动(iot-device DeviceCommandListener): - 区分业务可预期异常与系统异常: BusinessException(如"请先在个人中心绑定涂鸦账号")改为 WARN 单行输出,不再打印堆栈 - 新增 60 秒日志抑制窗口:同一 deviceId 在窗口内只详细记录一次,其余静默累计; 下一个窗口首次记录时汇总上一周期被抑制的次数,避免高频场景刷屏 - 系统异常保留 ERROR + 堆栈(窗口内首次),同样受窗口抑制保护 生产验证:重启后日志变为 WARN「设备控制指令业务异常: 请先在个人中心绑定涂鸦账号(上一周期同类异常已抑制 N 次)」, 不再输出堆栈;RabbitMQ 队列深度为 0;4 个服务 active。
This commit is contained in:
parent
b2d758be33
commit
56a40edd8e
@ -1,5 +1,6 @@
|
|||||||
package com.iot.device.event;
|
package com.iot.device.event;
|
||||||
|
|
||||||
|
import com.iot.common.exception.BusinessException;
|
||||||
import com.iot.device.config.RabbitConfig;
|
import com.iot.device.config.RabbitConfig;
|
||||||
import com.iot.device.entity.Device;
|
import com.iot.device.entity.Device;
|
||||||
import com.iot.device.mapper.DeviceMapper;
|
import com.iot.device.mapper.DeviceMapper;
|
||||||
@ -12,6 +13,7 @@ import org.springframework.stereotype.Component;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备控制指令监听器
|
* 设备控制指令监听器
|
||||||
@ -23,6 +25,12 @@ import java.util.Map;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class DeviceCommandListener {
|
public class DeviceCommandListener {
|
||||||
|
|
||||||
|
/** 日志抑制窗口:同一 key 在该窗口内只详细记录一次,其余静默累计 */
|
||||||
|
private static final long LOG_SUPPRESS_WINDOW_MS = 60_000L;
|
||||||
|
|
||||||
|
/** key -> {窗口起始时间, 窗口内被抑制次数, 上一窗口被抑制次数} */
|
||||||
|
private final Map<String, long[]> logThrottle = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private final DeviceMapper deviceMapper;
|
private final DeviceMapper deviceMapper;
|
||||||
private final TuyaCloudService tuyaCloudService;
|
private final TuyaCloudService tuyaCloudService;
|
||||||
|
|
||||||
@ -90,8 +98,52 @@ public class DeviceCommandListener {
|
|||||||
} else {
|
} else {
|
||||||
log.info("平台[{}]控制指令暂未适配: deviceId={}", platform, deviceId);
|
log.info("平台[{}]控制指令暂未适配: deviceId={}", platform, deviceId);
|
||||||
}
|
}
|
||||||
|
} catch (BusinessException be) {
|
||||||
|
// 业务可预期异常(如用户尚未绑定涂鸦账号):属于账号/数据状态问题而非系统故障,
|
||||||
|
// 不打印堆栈,并按 60 秒窗口抑制,避免定时场景高频触发时刷爆错误日志
|
||||||
|
logThrottled("biz:" + message.get("deviceId"), true, "设备控制指令业务异常", be);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("执行设备控制指令失败: {}", message, e);
|
logThrottled("err:" + message.get("deviceId"), false, "执行设备控制指令失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 受控日志输出:同一 key 在 60 秒窗口内只详细记录一次,其余静默累计;
|
||||||
|
* 下一个窗口首次记录时,汇总上一窗口被抑制的次数。
|
||||||
|
*
|
||||||
|
* @param business true=业务可预期异常(WARN 单行,不打堆栈);false=系统异常(ERROR + 堆栈)
|
||||||
|
*/
|
||||||
|
private void logThrottled(String key, boolean business, String message, Throwable t) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
long[] slot = logThrottle.compute(key, (k, v) -> {
|
||||||
|
if (v == null) {
|
||||||
|
return new long[]{now, 0, 0};
|
||||||
|
}
|
||||||
|
if (now - v[0] >= LOG_SUPPRESS_WINDOW_MS) {
|
||||||
|
// 新窗口开启:继承上一窗口被抑制的次数,用于汇总输出
|
||||||
|
return new long[]{now, 0, v[1]};
|
||||||
|
}
|
||||||
|
v[1]++;
|
||||||
|
return v;
|
||||||
|
});
|
||||||
|
if (slot[1] != 0) {
|
||||||
|
// 本窗口内已详细记录过一次,静默累计即可
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
long suppressed = slot[2];
|
||||||
|
slot[2] = 0;
|
||||||
|
if (business) {
|
||||||
|
if (suppressed > 0) {
|
||||||
|
log.warn("{}: {}(上一周期同类异常已抑制 {} 次)", message, t.getMessage(), suppressed);
|
||||||
|
} else {
|
||||||
|
log.warn("{}: {}", message, t.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (suppressed > 0) {
|
||||||
|
log.error("{}(上一周期同类异常已抑制 {} 次): {}", message, suppressed, t.getMessage(), t);
|
||||||
|
} else {
|
||||||
|
log.error("{}: {}", message, t.getMessage(), t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user