Java如何检测文件新增行?定时读取日志代码异常求排查
核心问题排查
你的代码无法正常运行是几个明确的逻辑和API使用错误导致的:
- 定时器周期配置错误:
Timer.scheduleAtFixedRate的延迟、周期参数单位都是毫秒,你传入的3000对应3秒间隔,和你预期的30秒不符,需要改为30000。 - 资源泄漏:
getLineCount方法中创建的Scanner没有手动关闭,每次执行任务都会占用文件句柄,运行数次后会因文件被锁定、句柄耗尽无法读取文件,直接抛出异常。 - 无效重复IO:单次任务里你先后两次调用
getLineCount(current)和getLineCount(f),等于同一个文件全量读4次,日志文件稍大就会出现明显卡顿,也增加了文件被占用的概率。 - 逻辑判断不可靠:仅靠对比文件行数判断新增内容存在缺陷,如果日志文件被截断、清空重写、滚动归档,会出现行数不变/下降的误判,完全识别不到内容变化;且你每次对比完成后才更新
current引用,首次执行的对比基准是初始化时的旧文件状态,逻辑顺序颠倒。
优化实现方案
不要每次全量扫描文件统计行数,更合理的实现是记录上一次读取到的文件字节偏移量,每次任务启动时直接从上次结束的位置开始读取,既能直接拿到新增的日志行,也不会重复读取旧内容,性能和准确率都远高于行数统计方案。
修正后的可运行代码如下:
import java.io.File; import java.io.RandomAccessFile; import java.util.Timer; import java.util.TimerTask; public class LogMonitor { // 记录上次读取到的文件指针位置,初始为0即从文件头开始读 private static long lastOffset = 0L; // 日志文件路径,统一配置避免重复写硬编码 private static final File LOG_FILE = new File("C:\\Users\\/*...*/latest.log"); public static void main(String args[]) { TimerTask task = new TimerTask() { @Override public void run() { // 先判断文件是否存在,避免文件被删除/移动时抛出异常 if (!LOG_FILE.exists() || !LOG_FILE.isFile()) { System.out.println("日志文件不存在,等待下次检测"); return; } try (RandomAccessFile raf = new RandomAccessFile(LOG_FILE, "r")) { long fileLength = raf.length(); // 如果当前文件长度小于上次记录的偏移量,说明文件被截断/重写,重置指针到文件头 if (fileLength < lastOffset) { lastOffset = 0L; } // 移动指针到上次读取结束的位置 raf.seek(lastOffset); String line; int newLineCount = 0; // 逐行读取新增内容 while ((line = raf.readLine()) != null) { newLineCount++; // 这里可以直接处理新增的日志行内容,比如打印、做规则匹配 System.out.println("新增日志行:" + line); } // 更新指针位置到当前文件末尾 lastOffset = raf.getFilePointer(); if (newLineCount > 0) { System.out.println("检测到" + newLineCount + "行新增日志"); } else { System.out.println("无新增日志"); } } catch (Exception e) { e.printStackTrace(); } System.out.println("-------------------"); } }; Timer timer = new Timer(); // 首次延迟3秒启动,之后每30秒执行一次 timer.scheduleAtFixedRate(task, 3000, 30000); System.out.println("日志监控已启动"); } }
额外优化建议
- 生产环境不建议用
Timer做定时任务,Timer是单线程执行,任意一个任务抛出未捕获异常会导致整个定时器终止,推荐用ScheduledExecutorService替代,异常隔离性更好。 - 日志读取时注意编码问题,
RandomAccessFile默认按ISO-8859-1读取中文会乱码,如果日志包含中文,可以把读到的字节按对应编码(比如UTF-8)转成字符串。 - 如果日志文件滚动生成新文件,可以增加文件inode(Linux)或文件创建时间(Windows)的判断,自动识别新的日志文件,重置读取指针。
内容的提问来源于stack exchange,提问作者Vantage
相关产品推荐
相关产品推荐

