|
|
# Redis 日志管理功能优化
|
|
|
|
|
|
## 问题描述
|
|
|
|
|
|
原始代码的日志管理逻辑存在问题:
|
|
|
- 只处理包含 `%` 的日志行,导致其他重要日志被忽略
|
|
|
- 对于进度日志的处理逻辑不清晰
|
|
|
- 没有正确实现"记录所有日志,但进度日志只保留最新"的需求
|
|
|
|
|
|
## 新的实现逻辑
|
|
|
|
|
|
### 核心需求
|
|
|
1. **记录所有日志**:不只是包含 `%` 的日志,所有训练日志都应该记录
|
|
|
2. **进度日志去重**:当遇到包含 `%` 的进度日志时,删除之前的进度日志,只保留最新的
|
|
|
3. **容量控制**:最多保存 50 条日志,超过时删除最旧的
|
|
|
4. **过期时间**:所有日志保存 5 天
|
|
|
|
|
|
### 修改后的方法
|
|
|
|
|
|
#### `manageTrainingLogInRedis`
|
|
|
|
|
|
```java
|
|
|
private void manageTrainingLogInRedis(Integer trainId, String logLine) {
|
|
|
try {
|
|
|
if (logLine == null || logLine.trim().isEmpty()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
String redisKey = "yolo:training_log:" + trainId;
|
|
|
boolean isProgressLog = logLine.contains("%");
|
|
|
|
|
|
if (isProgressLog) {
|
|
|
// 如果是进度日志(包含%),先删除之前的进度日志
|
|
|
removeProgressLogs(redisKey);
|
|
|
}
|
|
|
|
|
|
// 添加新的日志行到列表末尾
|
|
|
redisUtil.lSet(redisKey, logLine, 5 * 24 * 60 * 60); // 5天过期时间(秒)
|
|
|
|
|
|
// 检查列表长度,如果超过50条,删除第一条
|
|
|
long newSize = redisUtil.lGetListSize(redisKey);
|
|
|
if (newSize > 50) {
|
|
|
redisUtil.lRemove(redisKey, 1, redisUtil.lGetIndex(redisKey, 0));
|
|
|
}
|
|
|
|
|
|
log.debug("训练日志已存储到Redis - 训练ID: {}, 日志类型: {}, 当前日志数量: {}",
|
|
|
trainId, isProgressLog ? "进度日志" : "普通日志", redisUtil.lGetListSize(redisKey));
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
log.error("管理训练日志Redis操作失败 - 训练ID: {}", trainId, e);
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### `removeProgressLogs`
|
|
|
|
|
|
新增的辅助方法,专门用于删除所有进度日志:
|
|
|
|
|
|
```java
|
|
|
private void removeProgressLogs(String redisKey) {
|
|
|
try {
|
|
|
// 获取所有日志
|
|
|
java.util.List<Object> allLogs = redisUtil.lGet(redisKey, 0, -1);
|
|
|
|
|
|
// 找出并删除所有包含%的日志
|
|
|
for (int i = allLogs.size() - 1; i >= 0; i--) {
|
|
|
Object logObj = allLogs.get(i);
|
|
|
if (logObj != null) {
|
|
|
String logStr = logObj.toString();
|
|
|
if (logStr.contains("%")) {
|
|
|
redisUtil.lRemove(redisKey, 1, logStr);
|
|
|
log.debug("删除旧的进度日志: {}", logStr);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
log.error("删除进度日志时出错 - Redis键: {}", redisKey, e);
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
## 工作流程示例
|
|
|
|
|
|
### 场景1:普通日志
|
|
|
```
|
|
|
输入: "YOLO训练开始"
|
|
|
处理: 直接添加到Redis列表
|
|
|
结果: Redis列表: ["YOLO训练开始"]
|
|
|
```
|
|
|
|
|
|
### 场景2:进度日志(第一次)
|
|
|
```
|
|
|
输入: "Epoch 1/100: 50%|█████ | 100/200 [00:30<00:30]"
|
|
|
处理: 添加到Redis列表
|
|
|
结果: Redis列表: ["YOLO训练开始", "Epoch 1/100: 50%|█████ | 100/200 [00:30<00:30]"]
|
|
|
```
|
|
|
|
|
|
### 场景3:进度日志(更新)
|
|
|
```
|
|
|
输入: "Epoch 1/100: 75%|███████▌ | 150/200 [00:45<00:15]"
|
|
|
处理: 1. 删除之前的进度日志 2. 添加新的进度日志
|
|
|
结果: Redis列表: ["YOLO训练开始", "Epoch 1/100: 75%|███████▌ | 150/200 [00:45<00:15]"]
|
|
|
```
|
|
|
|
|
|
### 场景4:混合日志
|
|
|
```
|
|
|
输入序列:
|
|
|
1. "模型加载完成" -> 普通日志,直接添加
|
|
|
2. "Epoch 1/100: 25%|██▌ | 50/200 [00:15<00:45]" -> 进度日志,添加
|
|
|
3. "数据预处理完成" -> 普通日志,直接添加
|
|
|
4. "Epoch 1/100: 50%|█████ | 100/200 [00:30<00:30]" -> 进度日志,删除之前的进度日志并添加
|
|
|
|
|
|
最终Redis列表:
|
|
|
["模型加载完成", "数据预处理完成", "Epoch 1/100: 50%|█████ | 100/200 [00:30<00:30]"]
|
|
|
```
|
|
|
|
|
|
## 优势
|
|
|
|
|
|
1. **完整性**:所有重要日志都被保存,不会遗漏
|
|
|
2. **效率性**:进度日志只保留最新状态,避免重复
|
|
|
3. **可控性**:最多50条日志,防止Redis占用过多内存
|
|
|
4. **可维护性**:清晰的日志类型区分和调试信息
|
|
|
|
|
|
## 日志类型
|
|
|
|
|
|
- **普通日志**:训练开始、模型加载、错误信息等
|
|
|
- **进度日志**:包含 `%` 的进度条信息,如训练进度
|
|
|
|
|
|
## Redis 键格式
|
|
|
|
|
|
```
|
|
|
yolo:training_log:{trainId}
|
|
|
```
|
|
|
|
|
|
例如:
|
|
|
```
|
|
|
yolo:training_log:12345
|
|
|
```
|
|
|
|
|
|
## 过期时间
|
|
|
|
|
|
所有日志条目设置 5 天过期时间(432,000 秒),自动清理旧数据。 |