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

PHP实现类bash tail -n0 -F功能的日志持续读取脚本需求

用PHP实现类似tail -n0 -F的日志监控功能

需求

  • 脚本启动后仅读取新增行,不读取文件中已有的内容(对应bash命令tail -n0的效果)
  • 日志文件被轮转/清理后,脚本能自动恢复读取(对应bash命令tail -F的效果)

已实现的Bash版本

tail -n0 -F "$STATS_LOG_PATH" | \
while read LINE; do
    # 输出行或执行其他自定义操作
    echo "$LINE"
done

尝试的PHP代码(未达到预期)

// Open log file
$fh = fopen('/tmp/test.log', 'r');

while (true) {
    // Read line
    $line = fgets($fh);
    
    // Print line if any or sleep
    if ($line !== false) {
        echo $line . PHP_EOL;
    } else {
        // sleep for 0.1 seconds
        usleep(0.1 * 1000000);
        fseek($fh, ftell($fh));
    }
}

完整PHP实现代码

$logPath = '/tmp/test.log';
$fileHandle = null;
$lastInode = 0;

// 初始化/重新打开日志文件的工具函数
function openLogFile($path, &$inode) {
    global $fileHandle;
    // 先关闭旧的文件句柄(如果存在)
    if ($fileHandle !== null) {
        fclose($fileHandle);
    }

    // 等待文件重建(处理文件被删除后还未生成的情况)
    while (!file_exists($path)) {
        usleep(100000); // 休眠0.1秒
    }

    // 打开文件
    $fileHandle = fopen($path, 'r');
    if (!$fileHandle) {
        error_log("无法打开日志文件: $path");
        usleep(100000);
        return false;
    }

    // 跳到文件末尾,确保只读取后续新增内容
    fseek($fileHandle, 0, SEEK_END);
    // 获取当前文件的inode(用于检测文件轮转)
    $fileStat = fstat($fileHandle);
    $inode = $fileStat['ino'];

    return true;
}

// 初始化打开日志文件
openLogFile($logPath, $lastInode);

while (true) {
    // 检测文件是否被删除或轮转
    if (!file_exists($logPath)) {
        openLogFile($logPath, $lastInode);
        continue;
    }

    $currentStat = stat($logPath);
    if ($currentStat['ino'] !== $lastInode) {
        openLogFile($logPath, $lastInode);
        continue;
    }

    // 读取新增行
    $line = fgets($fileHandle);
    if ($line !== false) {
        // 这里可以替换为自定义的行处理逻辑
        echo rtrim($line) . PHP_EOL;
    } else {
        // 无新内容时休眠,避免CPU空转
        usleep(100000);
        // 重置指针位置,处理文件被截断的情况
        fseek($fileHandle, ftell($fileHandle));
    }
}

核心逻辑解释

  1. 仅读新增内容:启动时通过fseek($fileHandle, 0, SEEK_END)直接跳到文件末尾,完全跳过已有内容,和tail -n0效果一致。
  2. 应对文件轮转/清理:通过文件的inode编号判断文件是否被替换——日志轮转后新文件的inode会和旧句柄的inode不一致,此时自动重新打开新文件并跳到末尾;如果文件被删除,就循环等待直到文件重建后再继续。
  3. 降低CPU占用:无新内容时休眠0.1秒,避免脚本持续空转消耗CPU;休眠后重置指针位置,防止文件被清空后指针停在超出文件长度的位置。
  4. 容错处理:文件暂时不存在时不会直接报错退出,而是等待文件重建;打开失败时记录错误并短暂重试,提升脚本稳定性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 14:52:49