①首先創建websocket服務器
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @Author YMG
* @Date 2021/7/16 11:05
* @Description :
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thymeleaf.util.StringUtils;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Author YMG
* @Date 2021/7/22 18:37
* @Description :
*/
@SuppressWarnings(value = {"all"})
@Slf4j
@Component
@ServerEndpoint(value = "/websocket/log")
public class WebsocketLoggingServer {
private Process process;
private InputStream inputStream;
/**
* 連接集合
*/
private static final Map<String, Session> SESSION_MAP = new ConcurrentHashMap<>();
private static final Map<String, Integer> LENGTH_MAP = new ConcurrentHashMap<>();
/**
* 連接建立成功呼叫的方法
*/
@OnOpen
public void onOpen(Session session) {
//添加到集合中
SESSION_MAP.put(session.getId(), session);
//默認從第一行開始
LENGTH_MAP.put(session.getId(), 1);
//獲取日志資訊
new Thread(() -> {
log.info("WebSocketLoggingServer 任務開始");
boolean first = true;
while (SESSION_MAP.get(session.getId()) != null) {
BufferedReader reader = null;
try {
// 執行tail -f命令,填寫自己的 服務器日志地址,得到流決議
process = Runtime.getRuntime().exec("tail -n 200 nohup.log");
inputStream = process.getInputStream();
//字符流
reader = new BufferedReader(new InputStreamReader(inputStream));
Object[] lines = reader.lines().toArray();
//只取從上次之后產生的日志
Object[] copyOfRange = Arrays.copyOfRange(lines, LENGTH_MAP.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;
}
//存盤最新一行開始
LENGTH_MAP.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("WebSocketLoggingServer 任務結束");
}).start();
}
/**
* 連接關閉呼叫的方法
*/
@OnClose
public void onClose(Session session) {
//從集合中洗掉
SESSION_MAP.remove(session.getId());
LENGTH_MAP.remove(session.getId());
}
/**
* 發生錯誤時呼叫
*/
@OnError
public void one rror(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頁面連接websocket服務器獲取日志展示,由于我們用的是thymeleaf模版,請注意靜態資源的匯入方式(用到了jquery+layui)可以自行操作
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>logging</title>
<!-- 引入公用部分 -->
<script th:src="@{/static/jquery.js}"></script>
<link rel="stylesheet" th:href="@{/static/layui/css/layui.css}">
<script th:src="@{/static/layui/layui.js}"></script>
</head>
<body>
<!-- 顯示區 -->
<div id="loggingText" contenteditable="true"
style="width:100%;height: 890px;padding:0 30px;box-sizing:border-box;background-color: ghostwhite; overflow: auto;">
</div>
<!-- 操作欄 -->
<div style="text-align: center;" class="layui-btn-container">
<button class="layui-btn" onclick="$('#loggingText').text('')">清屏
</button>
<button class="layui-btn" onclick="$('#loggingText').animate({scrollTop:$('#loggingText')[0].scrollHeight});">滾動至底部
</button>
<button class="layui-btn" onclick="if(window.loggingAutoBottom){$(this).text('開啟自動滾動');}else{$(this).text('關閉自動滾動');}
window.loggingAutoBottom = !window.loggingAutoBottom">開啟自動滾動
</button>
</div>
</body>
<script>
//websocket物件
let websocket;
//判斷當前瀏覽器是否支持WebSocket
if ('WebSocket' in window) {
//服務器IP+埠
websocket = new WebSocket("ws://127.0.0.1:9027/websocket/log");
} else {
console.error("不支持WebSocket");
}
//連接發生錯誤的回呼方法
websocket.onerror = function () {
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>
</html>
以下是靜態資源+HTML頁面地址

由于這個專案用到了攔截器,要配置靜態資源及頁面放行 地址
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ResourceUtils;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
/**
* @Author YMG
* @Date 2021/6/2 16:35
* @Description :
*/
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor())
.addPathPatterns("/**")
/*放行所有請求*/
.excludePathPatterns("/**");
}
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
/*放行靜態資源與頁面*/
registry.addResourceHandler("/templates/**").addResourceLocations(ResourceUtils.CLASSPATH_URL_PREFIX+"/templates/");
registry.addResourceHandler("/static/**").addResourceLocations(ResourceUtils.CLASSPATH_URL_PREFIX+"/static/");
super.addResourceHandlers(registry);
}
@Override
public void addCorsMappings(CorsRegistry registry) {
/*解決請求跨域*/
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("POST", "GET", "PUT", "DELETE")
.allowCredentials(true)
.allowedHeaders("*")
.maxAge(3600);
}
@Bean
public AuthenticationInterceptor authenticationInterceptor() {
return new AuthenticationInterceptor();
}
}
③撰寫訪問controller
import com.zh.wisdom.config.security.token.PassToken;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
/**
* @Author YMG
* @Date 2021/7/16 11:18
* @Description :
*/
@RestController
@RequestMapping(value = "/websocket")
public class WebsocketController {
@PassToken
@GetMapping("/log")
public ModelAndView index(){
return new ModelAndView("log");
}
@PassToken
@GetMapping("/page")
public ModelAndView page(){
return new ModelAndView("test");
}
}
④專案打包部署到服務器,然后本地訪問controller查看日志
訪問地址"服務器ip:埠/websocket/log"
最終呈現效果如下

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/289878.html
標籤:其他
上一篇:基于SSH的寵物管理系統
下一篇:輸入6位數驗證碼的實作原理
