如何简单实现读取一个 txt 日志文件的前十行并删掉这十行,同时不能影响这个日志文件的实时写入,这个日志文件大概 1~ 2G 左右? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
frozenway
V2EX    PHP

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

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

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

    第 1 条附言   
    我是想做成类似于出栈那样,每次读十行然后删掉这十行,下一次程序运行也是读取接下来的十行并删掉
    第 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+ 树管理每一行的精确位置,自己管理文件被废弃的部分的回收再利用,自己管理文件存储碎片。也就是自己创造一种“数据库”。
    6ufq0VLZn0DDkL80
        19
    6ufq0VLZn0DDkL80  
       2020-05-04 15:50:46 +08:00
    日志库不都有设置单个文件大小和 rotate 文件的功能吗?
    6ufq0VLZn0DDkL80
        20
    6ufq0VLZn0DDkL80  
       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     Solana     916 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 37ms UTC 22:57 PVG 06:57 LAX 15:57 JFK 18:57
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86