You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复Java日志分析器开发中的ArrayIndexOutOfBoundsException错误

修复ArrayIndexOutOfBoundsException错误的方案

错误原因分析

报错Index 8 out of bounds for length 7意味着:当处理包含sched: Allocate或_slurm_rpc_kill_job的日志行时,通过line.split(" ")得到的数组parts长度小于9(索引从0开始,parts[8]是第9个元素),直接访问该索引会触发数组越界异常。

同时你的代码还有其他逻辑问题需要一并修复:

  • 嵌套的while循环会直接读取到文件末尾,导致外层循环无法处理后续行
  • reader.close()放在外层循环内部,第一次循环就关闭了输入流,后续操作会报错
  • 没有往logEntries列表中添加任何元素,导致后续分区统计、错误用户统计结果全部为0
  • 重复创建SimpleDateFormat对象,影响性能

具体修复步骤

1. 数组索引访问前做长度校验

在访问parts[8]这类高索引元素前,先判断数组长度是否足够,避免越界:

else if (line.contains("sched: Allocate")) {
    if (parts.length > 8) {
        String partition = parts[8].substring(9);
        logEntries.add(new LogEntry(date, level, message, partition));
        partitionCount.put(partition, partitionCount.getOrDefault(partition, 0) + 1);
    }
} else if (line.contains("_slurm_rpc_kill_job")) {
    if (parts.length > 8) {
        String user = parts[8].substring(3);
        // 在这里处理用户杀作业的统计逻辑
    }
}

2. 修复循环逻辑与流管理

移除内层while循环,直接处理当前匹配的行;使用try-with-resources自动管理流,避免手动关闭时机错误:

// 用try-with-resources自动关闭流,无需手动调用close
try (BufferedReader reader = new BufferedReader(new FileReader(logFileName))) {
    String line;
    List<LogEntry> logEntries = new ArrayList<>();
    int createdCount = 0;
    int endedCount = 0;
    long totalTime = 0;
    // 提前创建日期格式化对象,复用避免重复初始化
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
    // 提前初始化分区计数器
    Map<String, Integer> partitionCount = new HashMap<>();
    partitionCount.put("EPYC", 0);
    partitionCount.put("Opteron", 0);
    partitionCount.put("GPU", 0);

    while ((line = reader.readLine()) != null) {
        if (line.startsWith("[2022-06-01T") && (line.contains("_slurm_rpc_submit_batch_job") || 
            line.contains("sched: Allocate") || line.contains("_slurm_rpc_kill_job") || line.contains("_job_complete"))) {
            
            String[] parts = line.split(" ");
            // 先检查基本数组长度,避免访问parts[0]/1/2时越界
            if (parts.length < 3) {
                continue; // 跳过格式异常的行
            }
            String timestamp = parts[0];
            String level = parts[1];
            String message = parts[2];
            Date date = dateFormat.parse(timestamp.substring(1, timestamp.length() - 1));

            if (line.contains("_slurm_rpc_submit_batch_job")) {
                createdCount++;
            } else if (line.contains("_job_complete")) {
                if (parts.length > 6) {
                    endedCount++;
                    String exitStatus = parts[6].substring(13);
                    if (exitStatus.equals("0")) {
                        // 注意:当前代码用同一个timestamp作为start和end,时间差为0,需从日志中提取真实开始时间
                        Date end = dateFormat.parse(timestamp.substring(1, timestamp.length() - 1));
                        // Date start = 从对应作业的启动日志行获取;
                        // long diffInMillies = Math.abs(end.getTime() - start.getTime());
                        // totalTime += diffInMillies;
                    }
                }
            } else if (line.contains("sched: Allocate")) {
                if (parts.length > 8) {
                    String partition = parts[8].substring(9);
                    logEntries.add(new LogEntry(date, level, message, partition));
                    partitionCount.put(partition, partitionCount.get(partition) + 1);
                }
            } else if (line.contains("_slurm_rpc_kill_job")) {
                if (parts.length > 8) {
                    String user = parts[8].substring(3);
                    // 处理用户杀作业的统计
                }
            }
        }
    }

    // 所有行处理完成后输出结果
    System.out.println("Number of jobs created: " + createdCount);
    System.out.println("Number of jobs ended: " + endedCount);
    if (endedCount > 0)
        System.out.println("Average execution time of jobs: " + totalTime / endedCount);
    
    System.out.println("Number of jobs by partition:");
    System.out.println("EPYC: " + partitionCount.get("EPYC"));
    System.out.println("Opteron: " + partitionCount.get("Opteron"));
    System.out.println("GPU: " + partitionCount.get("GPU"));

    // 错误用户统计
    Map<String, Integer> errorCount = new HashMap<>();
    for (LogEntry entry : logEntries) {
        if (entry.level.equals("error")) {
            String[] parts = entry.message.split(":");
            if (parts.length > 0) {
                String user = parts[0];
                errorCount.put(user, errorCount.getOrDefault(user, 0) + 1);
            }
        }
    }
    System.out.println("Number of jobs causing error and the corresponding user:");
    for (Map.Entry<String, Integer> entry : errorCount.entrySet()) {
        System.out.println(entry.getKey() + ": " + entry.getValue());
    }
} catch (Exception e) {
    e.printStackTrace();
}

3. 修正内部类访问问题

LogEntry作为非静态内部类,无法在静态main方法中直接实例化,需改为静态内部类:

static class LogEntry {
    Date timestamp;
    String level;
    String message;
    String partition;

    public LogEntry(Date timestamp, String level, String message, String partition) {
        this.timestamp = timestamp;
        this.level = level;
        this.message = message;
        this.partition = partition;
    }
}

4. 修复作业执行时间计算逻辑

当前代码中startTime和endTime使用同一个parts[0],导致时间差为0。你需要通过作业ID关联不同日志行,提取作业的实际启动时间和结束时间来计算真实耗时。

总结

核心修复点是在访问数组索引前校验数组长度,同时修正循环逻辑、流管理、内部类、列表填充等潜在问题,确保代码稳定运行。

内容的提问来源于stack exchange,提问作者Bear

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 00:40:39