
需求:web 服务里需要监测日志,服务是异步的,理论上最好用 ws 的方式传输更新数据,而不要让前端轮询。
linux 的 shell 下用 tail -f 可以完美满足需求,但是 shell 登录太麻烦了。
想要实现的效果是,给文件一个钩子,把它像流一样处理,文件不变的时候就 await 阻塞住,文件更新的话则得到 await 返回,返回的内容就是新增的内容(姑且目前认为只会新增)
类似于下面这样的伪代码这种感觉
app = Framework() @app.ws('/ws/log-tail') async def client_async_log(ws, file_name): async with ws.connect() as conn: file = Tail(file_name) while True: string = await file.update_content() await conn.send(string) 不知道有没有方式实现。按照我的想法似乎以前倒是用过类似 watchdog 的服务,文件更新后可以得到一个异步回调,但是只知道更新了,不知道更新了哪些内容啊
1 Lotussha 2021-12-07 17:50:40 +08:00 刚好我也需要这个功能 |
2 fgwmlhdkkkw 2021-12-07 17:55:17 +08:00 socket af_unix 可以吗?我没试过 |
3 hsfzxjy 2021-12-07 17:55:57 +08:00 via Android |
4 defunct9 2021-12-07 17:55:58 +08:00 没有 |
5 2i2Re2PLMaDnghL 2021-12-07 18:45:34 +08:00 基于文件系统的 notify 也是只知道更新了内容,不知道更新哪些内容。 tail.c 里似乎也是自行判断的,甚至 regular file 还可能会变小(提示 file truncated ),判断这种情况的代码处打上了这样的注释(有两处,略微不同): ``` /* XXX: This is only a heuristic, as the file may have also been truncated and written to if st_size >= size (in which case we ignore new data <= size). Though in the inotify case it's more likely we'll get separate events for truncate() and write(). */ ``` 所以实现方法就还是 inotify 之类的,然后再把它包装成你需要的样子。 |
6 qieqie 2021-12-07 19:06:14 +08:00 只考虑追加写的话,先 lseek 再一直 read 就行了(结合 notify ),os 会负责记住这个 fd 的 offset 的 |
7 akira 2021-12-07 19:21:00 +08:00 看看 filebeat 之类的是不是你要的 |
8 jaredyam 2021-12-07 20:03:31 +08:00 Tailing a File A Python version of 'tail -f' import time import os def follow(thefile): thefile.seek(0, os.SEEK_END) # End-of-file while True: line = thefile.readline() if not line: time.sleep(0.1) # Sleep briefly continue yield line Idea : Seek to the end of the file and repeatedly try to read new lines. If new data is written to the file, we'll pick it up. Copyright (C) 2018, http://www.dabeaz.com Example File: follow.py |
9 iyaozhen 2021-12-07 20:17:41 +08:00 之前写过一个,给 tail -f 套了个 epoll 的壳 https://github.com/iyaozhen/filebeat.py |
10 37Y37 2021-12-07 20:55:53 +08:00 写过,这里有教程有代码,基于 websocket 实现 https://blog.ops-coffee.cn/s/hqaprps7w3d-9seegqab2q https://blog.ops-coffee.cn/s/r5spytjrl0jjeauye4q_-q |
11 LeeReamond OP @iyaozhen @Lotussha @2i2Re2PLMaDnghL @qieqie @jaredyam 感谢楼上老哥回复,看了看代码,似乎核心代码是 popen 然后依赖于 popen.stdout 搞些事情。我自己试了试这个 fd 是可以 while True: p.stdout.readline()的,最简单的套个线程转协程就行了。。楼上老哥说也可以 epoll |
12 LeeReamond OP @iyaozhen 老哥我看你那个代码里,tail -f filename comment ,为啥把 comment 加在这个位置就可以显示在进程信息里,我在本地的 shell 里试了试不好使。还有你的 popen 设的 buffsize=-1 是干啥用的 |
13 iyaozhen 2021-12-07 23:16:39 +08:00 @LeeReamond 额,那个其实语法就是 tail -f 两个文件,第二个肯定不存在,但进程里面可以看见命令信息,防止运维给 kill 了 默认就是-1 negative bufsize (the default) means the system default of io.DEFAULT_BUFFER_SIZE will be used. |
14 so1n 2021-12-07 23:32:08 +08:00 aionotify 可以很好的解决 |
15 ClericPy 2021-12-08 01:08:11 +08:00 以前用过 Python 的第三方库 不过既然你都提到 tail -f 了, 为啥不用 tail -F 呢, 分分钟写个脚本给你管道符转发出去就行了 tail -F xxx.log | python3 serv.py 看完是不是感觉很无聊, 因为 aws 挂了我特么在加班找问题... |
16 Richard14 2021-12-08 01:15:25 +08:00 https://gist.github.com/RedmondLee/e92341616a020fbe1fed85903a264efc 试着写了个最小实现,用另一个线程封装 selector ,每次流更新后提醒主线程的事件循环去获取内容(因为 EVENT_READ 已经准备好,read 可以认为是非阻塞的)。 但是最后结果还是不太对,主线程的 call_soon 函数虽然正常执行,但是其过程中的 future.set_result(None)并不能触发主事件循环中 await 这个 future 的函数返回。或者说偶尔能触发,偶尔不能触发,搞不太清楚为什么。 |
17 qW7bo2FbzbC0 2021-12-10 14:40:06 +08:00 1.没有读取位置信息时,从开始,或者倒数 X KB 处读取,有上次读取位置信息时接着读 2.读到文件结尾时自动结束,并记录位置信息,等待间隔后,再次读取位置信息接着读 3.甚至读取前调用 stat 记录文件 inode 信息,inode 变化后,重置位置信息,从零(开头)开始读 考虑到大日志文件的性能问题,我使用了 go 进行实现 考虑到大量数据传输,与解耦问题,我使用了 kafka 进行中转 考虑到超长行问题,我使用了 kafka 的压缩算法 ==> cpu 和内存占用略高 然后就是,需要定义行切割和碎片行合并问题 |
18 fighterhit 2021-12-12 02:29:46 +08:00 无意间看到有个 golang 版的 tail -f 实现:github.com/hpcloud/tail ,可以参考下 |