You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

143 lines
4.6 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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 秒),自动清理旧数据。