Java用WebSocket + tail命令实现Web实时日志

技术 · 2019-08-30

在Linux操作系统中,经常需要查看日志文件的实时输出内容,通常会使用tail -f或者tailf命令。查看实时日志可能会需要首先SSH连上Linux主机,步骤很麻烦不说,如果是生产环境的服务器,可能还会控制各种权限。基于Web的实时日志可以解决这个问题。
由于传统的HTTP协议是请求/响应模式,而实时日志需要不定时的持续的输出,由服务器主动推送给客户端浏览器。所以这里使用的是HTML5的WebSocket协议。

Java后台
JSR 356是Java实现WebSocket的一套规范,所以需要一个支持JSR 356的服务器,例如Tomcat、Jetty的最新版本。
JSR 356提供了注解@ServerEndpoint,并需要指定一个路径,用于处理客户端WebSocket请求。
import java.io.IOException;
import java.io.InputStream;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/log")
public class LogWebSocketHandle {

private Process process;
private InputStream inputStream;

/**
 * 新的WebSocket请求开启
 */
@OnOpen
public void onOpen(Session session) {
    try {
        // 执行tail -f命令
        process = Runtime.getRuntime().exec("tail -f /var/log/syslog");
        inputStream = process.getInputStream();

        // 一定要启动新的线程,防止InputStream阻塞处理WebSocket的线程
        TailLogThread thread = new TailLogThread(inputStream, session);
        thread.start();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

/**
 * WebSocket请求关闭
 */
@OnClose
public void onClose() {
    try {
        if(inputStream != null)
            inputStream.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    if(process != null)
        process.destroy();
}

@OnError
public void onError(Throwable thr) {
    thr.printStackTrace();
}

}

由于针对每个WebSocket连接都会创建一个新的LogWebSocketHandle实例,所以可以不用像Servlet一样考虑线程安全问题。由于tail -f命令的输入流会阻塞当前线程,所以一定要创建一个新的线程来读取tail -f命令的返回结果:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import javax.websocket.Session;

public class TailLogThread extends Thread {

private BufferedReader reader;
private Session session;

public TailLogThread(InputStream in, Session session) {
    this.reader = new BufferedReader(new InputStreamReader(in));
    this.session = session;

}

@Override
public void run() {
    String line;
    try {
        while((line = reader.readLine()) != null) {
            // 将实时日志通过WebSocket发送给客户端,给每一行添加一个HTML换行
            session.getBasicRemote().sendText(line + "<br>");
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

}

Web前端
Web前端需要通过WebSocket连接到服务端,实时接收最新的日志内容并展示到页面上。
<!DOCTYPE html>



tail log


<div id="log-container" style="height: 450px; overflow-y: scroll; background: #333; color: #aaa; padding: 10px;">
    <div>
    </div>
</div>


完成编码后,就可以部署了。由于用到tail命令,该项目需要部署在Linux系统上。

Theme Jasmine by Kent Liao