frozenway
V2EX  ›  PHP

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

  •  
  •   frozenway · May 4, 2020 · 8727 views
    This topic created in 2376 days ago, the information mentioned may be changed or developed.

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

    Supplement 1  ·  May 4, 2020
    我是想做成类似于出栈那样,每次读十行然后删掉这十行,下一次程序运行也是读取接下来的十行并删掉
    Supplement 2  ·  May 4, 2020
    <?php
    $file = '/opt/nginx/log/default.log';
    $content = shell_exec('head -n 10 '.$file);
    echo $content;
    shell_exec('sed -i \'10d\' '.$file);
    

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

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

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

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

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

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

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

    touch 日志路径

    nginx -s reopen
    hstdt
    hstdt
    May 4, 2020 via iPhone
    27
    要是我来做,我可能会使用 sqlite😉
    ETiV
    ETiV
    May 4, 2020 via iPhone
    28
    mkfifo ?
    asilin
    asilin
    May 4, 2020
    29
    很简单,用 sed -i -c 参数就可以完成,具体参见下面的链接:
    https://stackoverflow.com/questions/36930913/extra-null-characters-when-sed-edit-the-file-in-place-which-under-wirting
    banxiaobu
    banxiaobu
    May 4, 2020
    30
    我更感兴趣的是这个是什么样的背景需要这么玩
    burringcat
    burringcat
    May 4, 2020
    31
    直接用 shell 就行,不要再用 php 把 shell 包起来!!!
    tlday
    tlday
    May 4, 2020
    32
    我没有搞懂为什么要删掉,假如不需要删掉
    你可以试试这个:
    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
    muzuiget
    May 4, 2020
    33
    #25 果然是 X/Y 问题。
    tlday
    tlday
    May 4, 2020
    34
    我上面的例子,如果你不手动杀掉 cat 进程,或者关掉用来保持 cat 进程的 reader 进程,就可以“无论间隔多久,下一次程序运行也是读取接下来的十行”
    tlday
    tlday
    May 4, 2020
    35
    不想文件越来越大是典型的 log rotation 的应用场景,你这是两个需求。
    msg7086
    msg7086
    May 4, 2020
    36
    你 sed 途中遇上 nginx 写入的话你就等着丢数据吧。
    (丢的概率有多大呢?这么说吧,流量高的站百分百丢数据。)

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

    楼主你这种日志的打开方式说不好听点纯粹是没事干……
    sparkmlib
    sparkmlib
    May 4, 2020
    46
    logstash + ES + kinba
    roychan
    roychan
    May 5, 2020
    47
    @cholerae 可以用 FALLOC_FL_COLLAPSE_RANGE,这样文件中间就没有洞了。
    CoderGeek
    CoderGeek
    May 5, 2020
    48
    可以将文件分段存储 1 天一个分为大小或后缀的多个文件 nginx 也可以设置 再用 shell 直接读取都可以的 你这个删除在写入的思路不太合适
    fensou
    fensou
    May 5, 2020 via iPhone
    49
    @sparkmlib logstash 换 filebeat+nginx module,连 dash 都自带了
    fensou
    fensou
    May 5, 2020 via iPhone
    50
    graylog 其实更方便
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   1572 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 91ms · UTC 23:38 · PVG 07:38 · LAX 15:38 · JFK 18:38
    ♥ Do have faith in what you're doing.