如何修复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
相关产品推荐
相关产品推荐

