因为最近在做课程设计,然后采用了前后端分离技术进行开发,后端完成之后被我放到了阿里云服务器上,代码肯定有出问题的时候,刚开始每一次出问题我都跑去连接服务器,然后看问题,(一方面因为我懒没在上面挂专门的日志监控的,另一方面是部署后端的机器是学生机跑太多会影响机器的效率)非常麻烦,所以我就突发奇想做一个类似这个的日志监控程序,放在管理后台里面,在出错的时候用鲜明的颜色标注出来,便于查找问题。于是我就跑去研究折腾这个东西,还好网上有可以参考的地方。经过了2天的折腾已经初步完成了日志监控。
后端框架:springboot
前端框架:vue
通信方式:websocket
前端依赖:
npm install stompjs
运行npm run dev可能会报错,提示安装net,执行命令
npm install --save net
后端依赖:
这里两种方式都可以,具体你的框架是采用哪种方式搭建的。
maven:
org.springframework.boot
spring-boot-starter-websocket
gradle:
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-websocket', version: '2.1.5.RELEASE'
注:因为我搭建的springboot是基于2.1.5的所以我后面标明了版本,如果你是其他版本的,也可以修改。
实现原理十分简单,一个过滤器(LogFilter),一个socket配置(WebSocketConfig),一个消息实体(LoggerMessage),一个任务队列(LoggerQueue),一个日志配置文件(logback.xml)即可完成该功能。
具体原理如图所示:
openSocket() {
if (this.stompClient == null) {
this.res = "通道连接成功,静默等待...."
// this.$refs['logContainerDiv'].append();
// 建立连接对象
let socket = new SockJS('http://127.0.0.1:8080/websocket?token=kl');
// 获取STOMP子协议的客户端对象
this.stompClient = Stomp.over(socket);
this.stompClient.connect({token: 'kl'}, () => {
this.stompClient.subscribe('/topic/pullLogger',(event) => {
let content = JSON.parse(event.body);
let leverhtml = '';
let className = ""+content.className + "";
switch (content.level) {
case 'INFO':
leverhtml = "" +content.level + "";
break;
case 'DEBUG':
leverhtml = "" +content.level + "";
break;
case 'WARN':
leverhtml = "" +content.level + "";
break;
case 'ERROR':
leverhtml = "" +content.level + "";
break;
}
this.res+= "" + content.timestamp + " " +leverhtml + " --- [" + content.threadName + "] " + className + " :" + content.body + ""
// this.$refs['logContainerDiv'].append(content.timestamp + " " + leverhtml + " --- [" + content.threadName + "] " + className + " :" + content.body + "
");
if (content.exception != "") {
this.res+= "" + content.exception + ""
// this.$refs['logContainerDiv'].append();
}
if (content.cause != "") {
this.res+= "" + content.cause + ""
// this.$refs['logContainerDiv'].append(content.cause);
}
// this.$refs['logContainer'].scrollTo(this.$refs['logContainerDiv'].height() - this.$refs['logContainer'].height());
},
{
token: "kltoen"
});
});
}
},
closeSocket() {
if (this.stompClient != null) {
this.stompClient.disconnect();
this.stompClient = null;
}
},
后端部分在websocket模块,需要在对应的类上面加入@EnableWebSocketMessageBroker注解,如果不加该注解在运行程序的时候SimpMessagingTemplate类将抛出空指针异常,无法注入。
/**
* @author gjt
* @version 1.0
* @date 2019/6/24 21:13
* @Description 这里写描述内容
*/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Autowired
private SimpMessagingTemplate messagingTemplate;
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket")
.setAllowedOrigins("*")
.withSockJS();
}
/**
* 推送日志到/topic/pullLogger
*/
@PostConstruct
public void pushLogger(){
ExecutorService executorService= Executors.newFixedThreadPool(2);
Runnable runnable=new Runnable() {
@Override
public void run() {
while (true) {
try {
LoggerMessage log = LoggerQueue.getInstance().poll();
if(log!=null){
if(messagingTemplate!=null)
{
messagingTemplate.convertAndSend("/topic/pullLogger",log);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
executorService.submit(runnable);
}
}
/**
* @author gjt
* @version 1.0
* @date 2019/6/24 21:12
* @Description 这里写描述内容
*/
@Service
public class LogFilter extends Filter<ILoggingEvent> {
@Override
public FilterReply decide(ILoggingEvent event) {
String exception = "";
IThrowableProxy iThrowableProxy1 = event.getThrowableProxy();
if(iThrowableProxy1!=null){
exception = ""+iThrowableProxy1.getClassName()+" "+iThrowableProxy1.getMessage()+"";
for(int i=0; i<iThrowableProxy1.getStackTraceElementProxyArray().length;i++){
exception += ""+iThrowableProxy1.getStackTraceElementProxyArray()[i].toString()+"";
}
}
LoggerMessage loggerMessage = new LoggerMessage(
event.getMessage()
, DateFormat.getDateTimeInstance().format(new Date(event.getTimeStamp())),
event.getThreadName(),
event.getLoggerName(),
event.getLevel().levelStr,
exception,
""
);
LoggerQueue.getInstance().push(loggerMessage);
return FilterReply.ACCEPT;
}
}
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}pattern>
<charset>UTF-8charset>
encoder>
<filter class="org.rc.base.filter.LogFilter">filter>
appender>
spring-boot推送实时日志到前端页面显示https://blog.csdn.net/u014174854/article/details/82143595
spring boot集成WebSocket https://blog.csdn.net/zhanghui3239619/article/details/78608662
vue中使用stompjs实现mqtt消息推送通知 https://www.cnblogs.com/liemei/p/7064386.html
代码我没有放全,如果有需要的可以留言,我鼓励大家更多的是主动去尝试写完代码。
以上代码存在一个问题就是实时性,就是日志只能实时反馈不能看几分钟以内的,这一点需要改进,也是我后期打算优化的地方。
嘎嘎嘎我要准备升级了。。。。考完试就干!