需求: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 1
基于文件系统的 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 ,可以参考下
|