Tailing a log file is a common task when we deploy or maintain some software in production. Instead of logging into the server and
tail -f, it would be nice if we can tail a log file in the browser. With WebSocket, this can be done easily. In this article, I’ll walk you through a simple logviewer (source) utility that is written in Python.
WebSocket is standard protocol over TCP, that provides full-duplex communication between client and server side, usually a browser and a web server. Before WebSocket, when we want to keep an alive browser-server connection, we choose from long polling, forever frame or Comet techniques. Now that WebSocket is widely supported by major browsers, we can use it to implement web chatroom, games, realtime dashboard, etc. Besides, WebSocket connection can be established by an HTTP upgrade request, and communicate over 80 port, so as to bring minimum impact on existing network facility.
websockets is a Python package that utilize Python’s
asyncio to develop WebSocket servers and clients. The package can be installed via
pip, and it requires Python 3.3+.
pip install websockets
Following is a simple Echo server:
Here we use Python’s coroutines to handle client requests. Coroutine enables single-threaded application to run concurrent codes, such as handling socket I/O. Note that Python 3.5 introduced two new keywords for coroutine,
await, so the Echo server can be rewritten as:
async def echo(websocket, path):
For client side, we use the built-in
let ws = new WebSocket('ws://localhost:8765')
We’ll take the following steps to implement a log viewer:
- Client opens a WebSocket connection, and puts the file path in the url, like
- Server parses the file path, along with a flag that indicates whether this is a view once or tail request;
- Open file and start sending contents within a for loop.
Full code can be found on GitHub, so here I’ll select some important parts:
- Sometimes the client browser will not close the connection properly, so it’s necessary to add some heartbeat mechanism. For instance:
if time.time() - last_heartbeat > HEARTBEAT_INTERVAL:
- Log files may contain ANSI color codes (e.g. logging level). We can use
ansi2htmlpackage to convert them into HTML:
from ansi2html import Ansi2HTMLConverter
- It’s also necessary to do some permission checks on the file path. For example, convert to absolute path and do a simple prefix check.