V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
frozenway
V2EX  ›  PHP

如何简单实现读取一个 txt 日志文件的前十行并删掉这十行,同时不能影响这个日志文件的实时写入,这个日志文件大概 1~ 2G 左右?

  •  
  •   frozenway · 2020-05-04 14:35:24 +08:00 · 7385 次点击
    这是一个创建于 1698 天前的主题,其中的信息可能已经有所发展或是发生改变。

    用 fread 先读出来,去掉再 fwrire 覆盖原来的,会不会造成期间写入文件的日志信息?

    第 1 条附言  ·  2020-05-04 15:14:40 +08:00
    我是想做成类似于出栈那样,每次读十行然后删掉这十行,下一次程序运行也是读取接下来的十行并删掉
    第 2 条附言  ·  2020-05-04 16:07:08 +08:00
    <?php
    $file = '/opt/nginx/log/default.log';
    $content = shell_exec('head -n 10 '.$file);
    echo $content;
    shell_exec('sed -i \'10d\' '.$file);
    

    没办法,只能用shell来实现了

    第 3 条附言  ·  2020-05-05 09:47:20 +08:00
    好的,谢谢各位大佬,确实是把方向搞错了
    50 条回复    2020-05-05 11:45:52 +08:00
    jmc891205
        1
    jmc891205  
       2020-05-04 14:44:24 +08:00 via iPhone   ❤️ 3
    说说看为什么会有这种需求
    可能有更好的解决办法
    frozenway
        2
    frozenway  
    OP
       2020-05-04 14:56:49 +08:00
    @jmc891205 想逐行分析 nginx 日志,看看访客的详细信息
    littlewing
        3
    littlewing  
       2020-05-04 14:59:28 +08:00
    @frozenway 那为什么要删掉前几行?
    frozenway
        4
    frozenway  
    OP
       2020-05-04 15:02:07 +08:00
    @littlewing 不删掉,下次再读取前十行还是它啊
    ic2y
        5
    ic2y  
       2020-05-04 15:04:05 +08:00
    那为什么不用 fseek
    Jooooooooo
        6
    Jooooooooo  
       2020-05-04 15:06:17 +08:00
    既然是读取分析的需求, 那分析的时候跳过前十行不就行了?
    eason1874
        7
    eason1874  
       2020-05-04 15:06:50 +08:00
    fseek + fgets 分块读取就行了,用不着动文件。

    比如一次读 100KB,如果最后一行不完整就留着,下次读取块之后加在前面就行。
    jimmyismagic
        8
    jimmyismagic  
       2020-05-04 15:12:22 +08:00   ❤️ 2
    千万不要多线程处理文件,会乱掉的,哈哈
    sanggao
        9
    sanggao  
       2020-05-04 15:14:26 +08:00
    sed
    frozenway
        10
    frozenway  
    OP
       2020-05-04 15:16:16 +08:00
    @ic2y @eason1874 这个应该怎么写?
    @Jooooooooo 不是跳过那么简单
    frozenway
        11
    frozenway  
    OP
       2020-05-04 15:17:56 +08:00
    @sanggao sed 是 shell 命令吧?
    leido
        12
    leido  
       2020-05-04 15:21:49 +08:00
    tail -n +11 path_to_file > newfile
    mv newfile path_to_file
    frozenway
        13
    frozenway  
    OP
       2020-05-04 15:24:25 +08:00
    @leido 这个只是删除前十行吧,我还要读取前十行呢
    vk42
        14
    vk42  
       2020-05-04 15:27:10 +08:00 via Android
    保存上次读完 10 行之后的位置,下次 fseek 不就行了,为啥要费劲删 10 行
    frozenway
        15
    frozenway  
    OP
       2020-05-04 15:27:16 +08:00
    “array_shift — 将数组开头的单元移出数组” ,有没有类似这样的文件出栈的功能函数?
    frozenway
        16
    frozenway  
    OP
       2020-05-04 15:29:14 +08:00
    @vk42 因为不想文件越来越大
    beastk
        17
    beastk  
       2020-05-04 15:31:57 +08:00 via iPhone
    按天生成日志不就得了
    ipwx
        18
    ipwx  
       2020-05-04 15:48:51 +08:00   ❤️ 2
    @frozenway 首先必须说明,我没听说过操作系统会提供“删掉文件前多少字节”这种功能。所以大概率楼主你的字面需求是完成不了的。

    但是解决方案有很多。比如:

    1 、固定文件大小,把整个文件当做一个 circular buffer 用(记满了从尾巴跳到开头再开始写,直接覆盖老的内容)。然后用一个额外的小文件记录你有效的起始指针和尾指针的文职。
    2 、不要用行这么大的粒度,粗略一点。比如每 500KB 换一个文件写。文件太多了就把老的文件删掉一个。
    3 、上数据库。
    4 、模仿数据库,自己用 B+ 树管理每一行的精确位置,自己管理文件被废弃的部分的回收再利用,自己管理文件存储碎片。也就是自己创造一种“数据库”。
    cholerae
        19
    cholerae  
       2020-05-04 15:50:46 +08:00
    日志库不都有设置单个文件大小和 rotate 文件的功能吗?
    cholerae
        20
    cholerae  
       2020-05-04 15:55:39 +08:00   ❤️ 1
    说回你的这个需求本身,有个系统调用是 fallocate,fallocate 支持一个参数叫 FALLOC_FL_PUNCH_HOLE,这个东西支持把指定 fd 的偏移区间的块给释放掉,这样就不占空间了。不过不会改文件的元信息,所以你去 ls 还是看到原本的大小,需要你自己维护一个偏移量。
    miao1007
        21
    miao1007  
       2020-05-04 15:58:27 +08:00 via iPhone
    logstash
    GrayXu
        22
    GrayXu  
       2020-05-04 16:41:38 +08:00
    @frozenway “不想文件越来越大”这个需求显然是独立的。再额外做日志切割就好了……
    love
        23
    love  
       2020-05-04 16:59:35 +08:00 via Android
    你就不能下次从第 20 行开始读起?每读 10 行就复制一整个上 G 的文件,这个效率是嫌服务器负载太低了吗
    winglight2016
        24
    winglight2016  
       2020-05-04 17:21:40 +08:00
    虽然我没用过 php,但是日志分析这么基础的需求,不用再自己造轮子了吧?
    julyclyde
        25
    julyclyde  
       2020-05-04 17:25:47 +08:00   ❤️ 7
    这是一个很典型的
    自行分析了需求然后给出错误解方案
    然后上网问怎么实现这个错误方案
    的案例
    myqoo
        26
    myqoo  
       2020-05-04 17:25:48 +08:00
    正好写过完全相同的案例~ 每隔一段时间执行就可以:

    logtime=$(date "+%Y-%m-%d-%H-%M-%S")

    mv 日志路径 备份目录 /$logtime.log

    touch 日志路径

    nginx -s reopen
    hstdt
        27
    hstdt  
       2020-05-04 17:25:50 +08:00 via iPhone
    要是我来做,我可能会使用 sqlite😉
    ETiV
        28
    ETiV  
       2020-05-04 17:27:27 +08:00 via iPhone
    mkfifo ?
    asilin
        29
    asilin  
       2020-05-04 17:43:53 +08:00
    很简单,用 sed -i -c 参数就可以完成,具体参见下面的链接:
    https://stackoverflow.com/questions/36930913/extra-null-characters-when-sed-edit-the-file-in-place-which-under-wirting
    banxiaobu
        30
    banxiaobu  
       2020-05-04 18:08:08 +08:00
    我更感兴趣的是这个是什么样的背景需要这么玩
    burringcat
        31
    burringcat  
       2020-05-04 18:20:15 +08:00
    直接用 shell 就行,不要再用 php 把 shell 包起来!!!
    tlday
        32
    tlday  
       2020-05-04 18:39:28 +08:00
    我没有搞懂为什么要删掉,假如不需要删掉
    你可以试试这个:
    head -10 access.log #读取前 10 行
    head -20 access.log | tail -10 #读取 10-20 行
    head -30 access.log | tail -10 #读取 20-30 行
    ......依此类推

    可能还有这个:
    mkfifo log_seeker
    cat access.log > log_seeker &
    exec 3< log_seeker
    head -10 log_seeker #读取前 10 个
    head -10 log_seeker #读取下 10 个
    head -10 log_seeker #读取下 10 个
    ...依此类推
    完了使用
    fg # 调出上面的 cat 进程 Ctrl+C 掉
    exec 3<&- # 关掉用来保持 cat 进程的 reader 进程,参考: https://unix.stackexchange.com/questions/366219/prevent-automatic-eofs-to-a-named-pipe-and-send-an-eof-when-i-want-it
    muzuiget
        33
    muzuiget  
       2020-05-04 18:42:49 +08:00
    #25 果然是 X/Y 问题。
    tlday
        34
    tlday  
       2020-05-04 18:42:58 +08:00
    我上面的例子,如果你不手动杀掉 cat 进程,或者关掉用来保持 cat 进程的 reader 进程,就可以“无论间隔多久,下一次程序运行也是读取接下来的十行”
    tlday
        35
    tlday  
       2020-05-04 19:06:20 +08:00
    不想文件越来越大是典型的 log rotation 的应用场景,你这是两个需求。
    msg7086
        36
    msg7086  
       2020-05-04 19:20:37 +08:00
    你 sed 途中遇上 nginx 写入的话你就等着丢数据吧。
    (丢的概率有多大呢?这么说吧,流量高的站百分百丢数据。)

    好好的 logrotate 不用,偏要走邪道……
    mostkia
        37
    mostkia  
       2020-05-04 20:09:54 +08:00
    php 相关的程序日志,可以存到 SQLite 里面啊,一般 Nginx+PHP 的环境都带的。操作真的方便一个数量级,虽然都带文件锁,但你写 TXT 绝对好不到哪里去。以后导入导出也方便。如果你是要存 nginx 的日志的话,直接每天分割日志不就行了,然后可以视文件大小或者日期进行旧文件管理。
    ClarkAbe
        38
    ClarkAbe  
       2020-05-04 20:17:24 +08:00 via iPhone
    删这 10 行用乐观锁,其他的保持原样读写
    lepig
        39
    lepig  
       2020-05-04 20:46:20 +08:00
    这好像 PHP 面试的时候 经常会问 怎么使用 PHP 处理大日志文件 擦
    dreamage
        40
    dreamage  
       2020-05-04 21:17:59 +08:00
    es 不香么
    changePro
        41
    changePro  
       2020-05-04 21:41:26 +08:00 via Android
    典型的错误案例,怎么解决都是错。
    vk42
        42
    vk42  
       2020-05-04 22:23:49 +08:00
    @frozenway 控制 log 文件大小不是 logrotate 的事,为啥要自己瞎搞? OS 的文件设计模型就是不适合从头部和中间删内容,不然 ftruncate 为啥不直接提供接口。如果你 log 文件本身上 G,你的这个解决方案真的是嫌 server 闲得没事干……
    hallDrawnel
        43
    hallDrawnel  
       2020-05-04 22:27:45 +08:00
    典型的 XY Problem 例子,还好第一个回帖的就把方向带回来了。
    tozp
        44
    tozp  
       2020-05-04 22:32:54 +08:00
    你实现了也不可取,实时操作 IO 占用太多资源,不如全放内存,周期性再写入文件。
    xuanbg
        45
    xuanbg  
       2020-05-04 23:09:17 +08:00
    日志的打开方式只有两种。一种是结构化后进行各种指标统计,你可以想象为一个监控大屏。另一种是用一个或若干个关键词搜索出相关的日志,你可以想象为系统出了问题通过日志排查错误。

    楼主你这种日志的打开方式说不好听点纯粹是没事干……
    sparkmlib
        46
    sparkmlib  
       2020-05-04 23:40:39 +08:00
    logstash + ES + kinba
    roychan
        47
    roychan  
       2020-05-05 06:22:40 +08:00
    @cholerae 可以用 FALLOC_FL_COLLAPSE_RANGE,这样文件中间就没有洞了。
    CoderGeek
        48
    CoderGeek  
       2020-05-05 10:03:28 +08:00
    可以将文件分段存储 1 天一个分为大小或后缀的多个文件 nginx 也可以设置 再用 shell 直接读取都可以的 你这个删除在写入的思路不太合适
    fensou
        49
    fensou  
       2020-05-05 11:45:09 +08:00 via iPhone
    @sparkmlib logstash 换 filebeat+nginx module,连 dash 都自带了
    fensou
        50
    fensou  
       2020-05-05 11:45:52 +08:00 via iPhone
    graylog 其实更方便
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   926 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 21:47 · PVG 05:47 · LAX 13:47 · JFK 16:47
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.