在生产环境运维工作中,查看线上服务器日志是一项常规工作。如果这项工作可以在浏览器中进行,而无需登录服务器执行 tail -f
命令,就太方便了。我们可以使用 WebSocket 技术轻松实现这一目标。在本文中,我将带各位一起使用 Python 编写一个日志查看工具。
WebSocket 简介
WebSocket 是一个标准化协议,构建在 TCP 之上,能够在客户端和服务端之间建立一个全双工的通信渠道。这里的客户端和服务端通常是用户浏览器和 Web 服务器。在 WebSocket 诞生之前,如果我们想保持这样的一个长连接,就需要使用诸如长轮询、永久帧、Comet 等技术。而现今 WebSocket 已经得到了所有主流浏览器的支持,我们可以使用它开发出在线聊天室、游戏、实时仪表盘等软件。此外,WebSocket 可以通过 HTTP Upgrade 请求来建立连接,并使用 80 端口通信,从而降低对现有网络环境的影响,如无需穿越防火墙。
websockets
Python 类库
websockets
是第三方的 Python 类库,它能基于 Python 提供的 asyncio
包来实现 WebSocket 服务端以及客户端应用。我们可以使用 pip
来安装它,要求 Python 3.3 以上的版本。
1 | pip install websockets |
下面是一段简单的 Echo 服务代码:
1 | import asyncio |
可以看到,我们使用 Python 的协程来处理客户端请求。协程是 Python 3.3 引入的新概念,简单来说,它能通过单个线程来实现并发编程,主要适用于处理套接字 I/O 请求等场景。Python 3.5 开始又引入了 async
和 await
关键字,方便程序员使用协程。以下是使用新关键字对 Echo 服务进行改写:
1 | async def echo(websocket, path): |
对于客户端应用,我们直接使用浏览器内置的 WebSocket
类。将下面的代码直接粘贴到 Chrome 浏览器的 JavaScript 控制台中就可以运行了:
1 | let ws = new WebSocket('ws://localhost:8765') |
查看并监听日志
我们将通过以下几步来构建日志查看器:
- 首先,客户端发起一个 WebSocket 请求,并将请求的文件路径包含在 URL 中,形如
ws://localhost:8765/tmp/build.log?tail=1
; - 服务端接受到请求后,将文件路径解析出来,顺带解析出是否要持续监听日志的标志位;
- 服务端打开日志文件,开始不断向客户端发送日志文件内容。
完整的源代码可以在 GitHub 中查看,以下只截取重要的部分:
1 |
|
其它特性
- 在实际应用中发现,浏览器有时不会正确关闭 WebSocket 连接,导致服务端资源浪费,因此我们添加一个简单的心跳机制:
1 | if time.time() - last_heartbeat > HEARTBEAT_INTERVAL: |
- 日志文件中有时会包含 ANSI 颜色高亮(如日志级别),我们可以使用
ansi2html
包来将高亮部分转换成 HTML 代码:
1 | from ansi2html import Ansi2HTMLConverter |
- 最后,日志文件路径也需要进行权限检查,本例中是将客户端传递的路径转换成绝对路径后,简单判断了路径前缀,以作权限控制。