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.

4.6 KiB

Redis 日志管理功能优化

问题描述

原始代码的日志管理逻辑存在问题:

  • 只处理包含 % 的日志行,导致其他重要日志被忽略
  • 对于进度日志的处理逻辑不清晰
  • 没有正确实现"记录所有日志,但进度日志只保留最新"的需求

新的实现逻辑

核心需求

  1. 记录所有日志:不只是包含 % 的日志,所有训练日志都应该记录
  2. 进度日志去重:当遇到包含 % 的进度日志时,删除之前的进度日志,只保留最新的
  3. 容量控制:最多保存 50 条日志,超过时删除最旧的
  4. 过期时间:所有日志保存 5 天

修改后的方法

manageTrainingLogInRedis

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

新增的辅助方法,专门用于删除所有进度日志:

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