用 fread 先读出来,去掉再 fwrire 覆盖原来的,会不会造成期间写入文件的日志信息?
<?php $file = '/opt/nginx/log/default.log'; $cOntent= shell_exec('head -n 10 '.$file); echo $content; shell_exec('sed -i \'10d\' '.$file);
没办法,只能用shell来实现了
![]() | 1 jmc891205 2020-05-04 14:44:24 +08:00 via iPhone ![]() 说说看为什么会有这种需求 可能有更好的解决办法 |
3 littlewing 2020-05-04 14:59:28 +08:00 @frozenway 那为什么要删掉前几行? |
![]() | 4 frozenway OP @littlewing 不删掉,下次再读取前十行还是它啊 |
5 ic2y 2020-05-04 15:04:05 +08:00 那为什么不用 fseek |
6 Jooooooooo 2020-05-04 15:06:17 +08:00 既然是读取分析的需求, 那分析的时候跳过前十行不就行了? |
![]() | 7 eason1874 2020-05-04 15:06:50 +08:00 fseek + fgets 分块读取就行了,用不着动文件。 比如一次读 100KB,如果最后一行不完整就留着,下次读取块之后加在前面就行。 |
8 jimmyismagic 2020-05-04 15:12:22 +08:00 ![]() 千万不要多线程处理文件,会乱掉的,哈哈 |
9 sanggao 2020-05-04 15:14:26 +08:00 sed |
![]() | 10 frozenway OP |
![]() | 12 leido 2020-05-04 15:21:49 +08:00 tail -n +11 path_to_file > newfile mv newfile path_to_file |
14 vk42 2020-05-04 15:27:10 +08:00 via Android 保存上次读完 10 行之后的位置,下次 fseek 不就行了,为啥要费劲删 10 行 |
![]() | 15 frozenway OP “array_shift 将数组开头的单元移出数组” ,有没有类似这样的文件出栈的功能函数? |
![]() | 17 beastk 2020-05-04 15:31:57 +08:00 via iPhone 按天生成日志不就得了 |
![]() | 18 ipwx 2020-05-04 15:48:51 +08:00 ![]() @frozenway 首先必须说明,我没听说过操作系统会提供“删掉文件前多少字节”这种功能。所以大概率楼主你的字面需求是完成不了的。 但是解决方案有很多。比如: 1 、固定文件大小,把整个文件当做一个 circular buffer 用(记满了从尾巴跳到开头再开始写,直接覆盖老的内容)。然后用一个额外的小文件记录你有效的起始指针和尾指针的文职。 2 、不要用行这么大的粒度,粗略一点。比如每 500KB 换一个文件写。文件太多了就把老的文件删掉一个。 3 、上数据库。 4 、模仿数据库,自己用 B+ 树管理每一行的精确位置,自己管理文件被废弃的部分的回收再利用,自己管理文件存储碎片。也就是自己创造一种“数据库”。 |
19 6ufq0VLZn0DDkL80 2020-05-04 15:50:46 +08:00 日志库不都有设置单个文件大小和 rotate 文件的功能吗? |
20 6ufq0VLZn0DDkL80 2020-05-04 15:55:39 +08:00 ![]() 说回你的这个需求本身,有个系统调用是 fallocate,fallocate 支持一个参数叫 FALLOC_FL_PUNCH_HOLE,这个东西支持把指定 fd 的偏移区间的块给释放掉,这样就不占空间了。不过不会改文件的元信息,所以你去 ls 还是看到原本的大小,需要你自己维护一个偏移量。 |
![]() | 21 miao1007 2020-05-04 15:58:27 +08:00 via iPhone logstash |
![]() | 23 love 2020-05-04 16:59:35 +08:00 via Android 你就不能下次从第 20 行开始读起?每读 10 行就复制一整个上 G 的文件,这个效率是嫌服务器负载太低了吗 |
![]() | 24 winglight2016 2020-05-04 17:21:40 +08:00 虽然我没用过 php,但是日志分析这么基础的需求,不用再自己造轮子了吧? |
25 julyclyde 2020-05-04 17:25:47 +08:00 ![]() 这是一个很典型的 自行分析了需求然后给出错误解方案 然后上网问怎么实现这个错误方案 的案例 |
![]() | 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 |
27 hstdt 2020-05-04 17:25:50 +08:00 via iPhone 要是我来做,我可能会使用 sqlite |
![]() | 28 ETiV 2020-05-04 17:27:27 +08:00 via iPhone mkfifo ? |
![]() | 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 |
![]() | 30 banxiaobu 2020-05-04 18:08:08 +08:00 我更感兴趣的是这个是什么样的背景需要这么玩 |
![]() | 31 burringcat 2020-05-04 18:20:15 +08:00 直接用 shell 就行,不要再用 php 把 shell 包起来!!! |
![]() | 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 |
![]() | 33 muzuiget 2020-05-04 18:42:49 +08:00 #25 果然是 X/Y 问题。 |
![]() | 34 tlday 2020-05-04 18:42:58 +08:00 我上面的例子,如果你不手动杀掉 cat 进程,或者关掉用来保持 cat 进程的 reader 进程,就可以“无论间隔多久,下一次程序运行也是读取接下来的十行” |
![]() | 35 tlday 2020-05-04 19:06:20 +08:00 不想文件越来越大是典型的 log rotation 的应用场景,你这是两个需求。 |
![]() | 36 msg7086 2020-05-04 19:20:37 +08:00 你 sed 途中遇上 nginx 写入的话你就等着丢数据吧。 (丢的概率有多大呢?这么说吧,流量高的站百分百丢数据。) 好好的 logrotate 不用,偏要走邪道…… |
![]() | 37 mostkia 2020-05-04 20:09:54 +08:00 php 相关的程序日志,可以存到 SQLite 里面啊,一般 Nginx+PHP 的环境都带的。操作真的方便一个数量级,虽然都带文件锁,但你写 TXT 绝对好不到哪里去。以后导入导出也方便。如果你是要存 nginx 的日志的话,直接每天分割日志不就行了,然后可以视文件大小或者日期进行旧文件管理。 |
![]() | 38 ClarkAbe 2020-05-04 20:17:24 +08:00 via iPhone 删这 10 行用乐观锁,其他的保持原样读写 |
![]() | 39 lepig 2020-05-04 20:46:20 +08:00 这好像 PHP 面试的时候 经常会问 怎么使用 PHP 处理大日志文件 擦 |
![]() | 40 dreamage 2020-05-04 21:17:59 +08:00 es 不香么 |
![]() | 41 changePro 2020-05-04 21:41:26 +08:00 via Android 典型的错误案例,怎么解决都是错。 |
42 vk42 2020-05-04 22:23:49 +08:00 @frozenway 控制 log 文件大小不是 logrotate 的事,为啥要自己瞎搞? OS 的文件设计模型就是不适合从头部和中间删内容,不然 ftruncate 为啥不直接提供接口。如果你 log 文件本身上 G,你的这个解决方案真的是嫌 server 闲得没事干…… |
![]() | 43 hallDrawnel 2020-05-04 22:27:45 +08:00 典型的 XY Problem 例子,还好第一个回帖的就把方向带回来了。 |
44 tozp 2020-05-04 22:32:54 +08:00 你实现了也不可取,实时操作 IO 占用太多资源,不如全放内存,周期性再写入文件。 |
![]() | 45 xuanbg 2020-05-04 23:09:17 +08:00 日志的打开方式只有两种。一种是结构化后进行各种指标统计,你可以想象为一个监控大屏。另一种是用一个或若干个关键词搜索出相关的日志,你可以想象为系统出了问题通过日志排查错误。 楼主你这种日志的打开方式说不好听点纯粹是没事干…… |
46 sparkmlib 2020-05-04 23:40:39 +08:00 logstash + ES + kinba |
![]() | 48 CoderGeek 2020-05-05 10:03:28 +08:00 可以将文件分段存储 1 天一个分为大小或后缀的多个文件 nginx 也可以设置 再用 shell 直接读取都可以的 你这个删除在写入的思路不太合适 |
49 fensou 2020-05-05 11:45:09 +08:00 via iPhone @sparkmlib logstash 换 filebeat+nginx module,连 dash 都自带了 |
50 fensou 2020-05-05 11:45:52 +08:00 via iPhone graylog 其实更方便 |