1、log4j.properties
log4j.rootLogger=debug,stdout,WriterAppender
#Console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%t] %c %L : %m%n
#log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} - %X{ip} -%5p %c{1}:%L - %m%n
# 选用WriterAppender作为Appender,表示以流的形式输出,这个Appender一般很少用,是常用Appender的父类
log4j.appender.WriterAppender=org.apache.log4j.WriterAppender
log4j.appender.WriterAppender.Threshold=debug
log4j.appender.WriterAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.WriterAppender.layout.ConversionPattern=%d %p [%t] %c %L : %m%n
# %X{ip} 输出本服务器的ip,通过MDC输入(见后面描述)
#log4j.appender.WA.layout.ConversionPattern=%d{ISO8601} - [%X{ip}] -%5p %c{1}:%L - %m%n
2、config
package com.yy.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* Created by yanyong on 2020/6/4.
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
3、处理器
package com.yy.config;
import org.apache.log4j.Appender;
import org.apache.log4j.Logger;
import org.apache.log4j.MDC;
import org.apache.log4j.WriterAppender;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.io.Writer;
import java.net.InetAddress;
/**
* Created by yanyong on 2020/6/5.
*/
@ServerEndpoint("/log")
@Component
public class LogSocketHandle {
private PipedReader reader;
private Writer writer;
/**
* WebSocket请求开启
*/
@OnOpen
public void onOpen(Session session) {
Logger root = Logger.getRootLogger();
try {
// 获取本服务器id
String hostAddress = InetAddress.getLocalHost().getHostAddress();
// MDC是key-value结构,有兴趣的可以去了解下,在log4j的配置中设置 %X{ip},在日志中输出
MDC.put("ip",hostAddress);
Appender appender = root.getAppender("WriterAppender");
// 通过管道流进行线程间的通讯
reader = new PipedReader();
writer = new PipedWriter( reader) ;
((WriterAppender) appender).setWriter(writer);
// 启动新的线程
LogThread thread = new LogThread(reader, session);
thread.start();
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* WebSocket请求关闭,关闭请求时调用此方法,关闭流
*/
@OnClose
public void onClose() {
try {
if(reader != null) {
reader.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
try {
if(writer != null) {
writer.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
@OnError
public void onError(Throwable thr) {
thr.printStackTrace();
}
}
4、线程
package com.yy.config;
import javax.websocket.Session;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PipedReader;
/**
* Created by yanyong on 2020/6/5.
*/
public class LogThread extends Thread {
private BufferedReader reader;
private Session session;
public LogThread(PipedReader pipedReader, Session session) {
this.reader = new BufferedReader(pipedReader);
this.session = session;
}
@Override
public void run() {
String line;
try {
while((line = reader.readLine()) != null) {
// 将实时日志通过WebSocket发送给客户端
session.getBasicRemote().sendText(line + "<br>");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
5、前端页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>日志</title>
</head>
<body>
<h1 style="text-align: center;">实时日志</h1>
<div id="log-container" contenteditable="true" class="content"
style="width: 100%;height: 500px;background-color: ghostwhite;overflow: auto;">
</div>
<div>
<!-- 下面div用的是miniui的样式,其他的稍微改变一下即可-->
<button style="left: 30px;top: 1px" onclick="clearContext()">清屏</button>
</div>
<script>
var ws = new WebSocket("ws://127.0.0.1:8080/log");
ws.onopen = function() {
ws.send("Hello WebSockets!");
};
ws.onmessage = function(evt) {
var content = document.getElementsByClassName('content')[0];
console.log(evt.data);
content.innerHTML += evt.data;
};
//关闭连接
ws.onclose = function() {
ws.close();
};
// 清屏日志
function clearContext() {
var content = document.getElementsByClassName('content')[0];
content.innerHTML = null;
}
</script>
</body>
</html>
6、github源码
源码下载