想要后端将日志实时返回给前端
尝试使用websocket
- 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
- 填写配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
/**
* 首先要注入ServerEndpointExporter,这个Bean会自动注册使用了@ServerEndPoint注解声明的WebSocket EndPoint。
* 要注意的是,如果使用独立的Servlet容器,而不是SpringBoot的内置容器,就不要注入ServerEndpointExporter,因为它将由容器自己提供和管理
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
- 调用类
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Api(tags = "日志管道")
@ServerEndpoint("/logChannel")
@Component
public class LogChannelController {
private static Session session;
/**
* 连接集合
*/
private static Map<String, Session> sessionMap = new ConcurrentHashMap<String, Session>();
private static Map<String, Integer> lengthMap = new ConcurrentHashMap<String, Integer>();
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session) {
//添加到集合中
sessionMap.put(session.getId(), session);
lengthMap.put(session.getId(), 1);//默认从第一行开始
//获取日志信息
new Thread(() -> {
log.info("LoggingWebSocketServer 任务开始");
boolean first = true;
while (sessionMap.get(session.getId()) != null) {
BufferedReader reader = null;
try {
//日志文件路径,获取最新的
String filePath = "D:/git_pro/AAAAA/logs/" + new SimpleDateFormat("yyyy-MM-dd").format(new Date()) + ".log";
//字符流
reader = new BufferedReader(new FileReader(filePath));
Object[] lines = reader.lines().toArray();
//只取从上次之后产生的日志
Object[] copyOfRange = Arrays.copyOfRange(lines, lengthMap.get(session.getId()), lines.length);
//对日志进行着色,更加美观 PS:注意,这里要根据日志生成规则来操作
for (int i = 0; i < copyOfRange.length; i++) {
String line = (String) copyOfRange[i];
//先转义
line = line.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll("\"", """);
//处理等级
line = line.replace("DEBUG", "<span style='color: blue;'>DEBUG</span>");
line = line.replace("INFO", "<span style='color: green;'>INFO</span>");
line = line.replace("WARN", "<span style='color: orange;'>WARN</span>");
line = line.replace("ERROR", "<span style='color: red;'>ERROR</span>");
//处理类名
String[] split = line.split("]");
if (split.length >= 2) {
String[] split1 = split[1].split("-");
if (split1.length >= 2) {
line = split[0] + "]" + "<span style='color: #298a8a;'>" + split1[0] + "</span>" + "-" + split1[1];
}
}
copyOfRange[i] = line;
}
//存储最新一行开始
lengthMap.put(session.getId(), lines.length);
//第一次如果太大,截取最新的200行就够了,避免传输的数据太大
if(first && copyOfRange.length > 200){
copyOfRange = Arrays.copyOfRange(copyOfRange, copyOfRange.length - 200, copyOfRange.length);
first = false;
}
String result = StringUtils.join(copyOfRange, "<br/>");
//发送
send(session, result);
//休眠一秒
Thread.sleep(1000);
} catch (Exception e) {
//捕获但不处理
e.printStackTrace();
} finally {
try {
reader.close();
} catch (IOException ignored) {
}
}
}
log.info("LoggingWebSocketServer 任务结束");
}).start();
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(Session session) {
//从集合中删除
sessionMap.remove(session.getId());
lengthMap.remove(session.getId());
}
/**
* 发生错误时调用
*/
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
/**
* 服务器接收到客户端消息时调用的方法
*/
@OnMessage
public void onMessage(String message, Session session) {
}
/**
* 封装一个send方法,发送消息到前端
*/
private void send(Session session, String message) {
try {
session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 前端html
<html>
<script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>
<script th:inline="javascript">
//websocket对象
let websocket = null;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:1919/logChannel");
} else {
console.error("不支持WebSocket");
}
//连接发生错误的回调方法
websocket.onerror = function (e) {
console.error("WebSocket连接发生错误");
};
//连接成功建立的回调方法
websocket.onopen = function () {
console.log("WebSocket连接成功")
};
//接收到消息的回调方法
websocket.onmessage = function (event) {
//追加
if (event.data) {
//日志内容
let $loggingText = $("#loggingText");
$loggingText.append(event.data);
//是否开启自动底部
if (window.loggingAutoBottom) {
//滚动条自动到最底部
$loggingText.scrollTop($loggingText[0].scrollHeight);
}
}
}
//连接关闭的回调方法
websocket.onclose = function () {
console.log("WebSocket连接关闭")
};
</script>
<body>
<div id = "loggingText">
</div>
</body>
</html>
以上就完成了代码,但是由于服务调用过程中,有拦截器,需要传入多个请求头,使用apipost测试好用,但是前端代码中无法传入多个请求头,无奈放弃websocket。
转为使用SseEmitter
https://blog.csdn.net/u011943534/article/details/120251614