feat(syslog): 增加 WAL 与分 IP 接入诊断

- 每分钟保存 WAL 全局状态和分 IP 接收统计,历史保留 7 天
- 记录接收、WAL 落盘、Kafka ACK、过滤、背压及丢弃数量
- 增加 wal_max_pending_bytes 和 wal_admission_unavailable 指标
- 增加 TCP pending 当前值及进程内峰值统计
- 网络异常后同一 IP 恢复接收时打印恢复日志
- 提供当前状态及按日期查询诊断历史的接口
- 连接关闭时清理 TCP pending 计数并停止无效重试
- 增加相关单元测试和现场排查文档
This commit is contained in:
王绍宏
2026-09-16 17:32:08 +08:00
parent faa9ffe2d9
commit 8f1a8b6283
30 changed files with 1722 additions and 91 deletions
@@ -1,6 +1,7 @@
package com.haobang.dispatch;
import com.haobang.config.AppProperties;
import com.haobang.diagnostics.SyslogIpMetricsRegistry;
import com.haobang.wal.SyslogWalRecord;
import com.haobang.wal.SyslogWalService;
import com.kafka.kafkaProducer;
@@ -8,6 +9,7 @@ import com.netty.SyslogMessageHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@@ -51,6 +53,7 @@ public class SyslogWalDispatcher {
private final LongAdder retryCount = new LongAdder();
private final LongAdder largeDispatched = new LongAdder();
private final AtomicLong inflight = new AtomicLong();
private SyslogIpMetricsRegistry ipMetrics;
private Semaphore permits;
private ByteBudget byteBudget;
@@ -71,6 +74,11 @@ public class SyslogWalDispatcher {
this.properties = properties;
}
@Autowired(required = false)
void setIpMetrics(SyslogIpMetricsRegistry ipMetrics) {
this.ipMetrics = ipMetrics;
}
@PostConstruct
public void start() throws Exception {
validateConfiguration();
@@ -157,6 +165,10 @@ public class SyslogWalDispatcher {
walService.recordTerminal(record, resolution.getType().name(), resolution.getReason());
} else {
terminalDropped.increment();
if (ipMetrics != null) {
ipMetrics.recordTerminalDropped(record.getSourceIp(), record.getSourcePort(),
record.getProtocol(), resolution.getDevice(), resolution.getReason());
}
}
terminalFiltered.increment();
markComplete(record);
@@ -200,6 +212,10 @@ public class SyslogWalDispatcher {
.whenComplete((delivery, failure) -> {
if (failure == null) {
kafkaAcked.increment();
if (ipMetrics != null) {
ipMetrics.recordKafkaAck(record.getSourceIp(), record.getSourcePort(),
record.getProtocol(), resolution.getDevice());
}
if (large) largeDispatched.increment();
markComplete(record);
} else {
@@ -214,6 +230,9 @@ public class SyslogWalDispatcher {
private void scheduleRetry(SyslogWalRecord record, int attempt, String stage, Throwable failure) {
retryCount.increment();
if (ipMetrics != null) {
ipMetrics.recordDispatchRetry(record.getSourceIp(), record.getSourcePort(), record.getProtocol());
}
long retries = retryCount.sum();
if (attempt == 1 || retries % 100 == 0) {
log.warn("WAL 事件投递失败,将保留原 event_id 重试: eventId={}, sequence={}, stage={}, "
@@ -261,18 +280,21 @@ public class SyslogWalDispatcher {
private void logStatus() {
if (!running.get()) return;
log.info("Syslog WAL状态: networkReceived={}, appended={}, admissionOversized={}, walPendingApprox={}, walPendingBytes={}, walQueue={}/{}, durableSequence={}, "
log.info("Syslog WAL状态: networkReceived={}, appended={}, admissionOversized={}, admissionUnavailable={}, "
+ "walPendingApprox={}, walPendingBytes={}/{}, walQueue={}/{}, durableSequence={}, "
+ "checkpointSequence={}, kafkaInflight={}, kafkaInflightBytes={}, kafkaAcked={}, largeDispatched={}, "
+ "terminalRecordEnabled={}, terminalFiltered={}, terminalDropped={}, "
+ "retry={}, tcpPauseTotal={}, udpDropped={}, deviceCacheSize={}, deviceCacheHits={}, "
+ "retry={}, tcpPauseTotal={}, tcpPendingFrames={}/{}, udpDropped={}, deviceCacheSize={}, deviceCacheHits={}, "
+ "deviceCacheMisses={}",
SyslogMessageHandler.getNetworkReceivedCount(), walService.getAppendedCount(),
walService.getOversizedCount(), walService.getPendingRecordsApprox(), walService.getPendingAppendBytes(),
walService.getOversizedCount(), walService.getUnavailableCount(),
walService.getPendingRecordsApprox(), walService.getPendingAppendBytes(), walService.getMaxPendingBytes(),
walService.getQueueSize(), walService.getQueueCapacity(), walService.getDurableSequence(),
walService.getCheckpointSequence(), inflight.get(), byteBudget == null ? 0L : byteBudget.used(),
kafkaAcked.sum(), largeDispatched.sum(), properties.isSyslogDispatchTerminalRecordEnabled(),
terminalFiltered.sum(), terminalDropped.sum(),
retryCount.sum(), SyslogMessageHandler.getTcpPauseCount(),
SyslogMessageHandler.getTcpPendingFramesCurrent(), SyslogMessageHandler.getTcpPendingFramesMax(),
SyslogMessageHandler.getUdpDroppedCount(), deviceResolver.getCacheSize(),
deviceResolver.getHitCount(), deviceResolver.getMissCount());
}