热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

springboot推送实时日志到前端页面显示

spring-boot推送实时日志到前端页面显示,Go语言社区,Golang程序员人脉社

个人技术网站 欢迎关注

网上有很多后台推送日志到前端页面的例子,这里我也借鉴了别人的做法 稍加改进一下。以前做前端页面显示日志一般都会想到ajax轮询去做,这样太耗费服务器资源了,性能也很差。使用长连接来做更稳妥,这就想到了用websocket。刚好spring-boot对websocket支持非常不错,使用很方便,接下来开整。

简单的spring-boot工程这里就不做过多的讲解了,只叙述核心部分

首先导入依赖


	org.springframework.boot
	spring-boot-starter-websocket

首先在项目的日志配置文件中新增/修改此节点配置


        
        
            ${CONSOLE_LOG_PATTERN}
            
            UTF-8
        
        
        

此节点配置主要是为了把控制台输出的日志交给我们自己的日志过滤器进行处理(此节点不要指定日志等级,记得)

接下来实现自己的日志过滤器


import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.FilterReply;
import com.xcloud.api.system.entity.LoggerMessage;
import org.springframework.stereotype.Service;

import java.text.DateFormat;
import java.util.Date;

/**
 * Xcloud-Api By IDEA
 * Created by LaoWang on 2018/8/25.
 */
@Service
public class LogFilter extends Filter {

    @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
"; } } 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; } }

这部分代码网上大部分都是一样的,但是并没有对异常具体信息进行处理,所以这里我把具体异常信息日志也添加进去了

创建一个阻塞队列,作为日志系统输出的日志的一个临时载体

import com.xcloud.api.system.entity.LoggerMessage;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;


public class LoggerQueue {
    //队列大小
    public static final int QUEUE_MAX_SIZE = 10000;
    private static LoggerQueue alarmMessageQueue = new LoggerQueue();
    //阻塞队列
    private BlockingQueue blockingQueue = new LinkedBlockingQueue<>(QUEUE_MAX_SIZE);

    private LoggerQueue() {
    }

    public static LoggerQueue getInstance() {
        return alarmMessageQueue;
    }

    /**
     * 消息入队
     *
     * @param log
     * @return
     */
    public boolean push(LoggerMessage log) {
        return this.blockingQueue.add(log);//队列满了就抛出异常,不阻塞
    }

    /**
     * 消息出队
     *
     * @return
     */
    public LoggerMessage poll() {
        LoggerMessage result = null;
        try {
            result = (LoggerMessage) this.blockingQueue.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return result;
    }
}

创建一个日志实体(这里我使用了lombok,如果没用的话,请自行生成get,set和全参数构造方法)

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

/**
 * 日志消息实体
 */
@Getter
@Setter
@ToString
@AllArgsConstructor
public class LoggerMessage {
    private String body;
    private String timestamp;
    private String threadName;
    private String className;
    private String level;
    private String exception;
    private String cause;
}

接下来配置WebSocket

import com.xcloud.api.system.core.LoggerQueue;
import com.xcloud.api.system.entity.LoggerMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import javax.annotation.PostConstruct;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Xcloud-Api By IDEA
 * 配置WebSocket消息代理端点,即stomp服务端
 * 为了连接安全,setAllowedOrigins设置的允许连接的源地址
 * 如果在非这个配置的地址下发起连接会报403
 * 进一步还可以使用addInterceptors设置拦截器,来做相关的鉴权操作
 * Created by LaoWang on 2018/8/25.
 */
@Slf4j
@Configuration
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);
    }
}

如果项目配置有拦截器获取其他权限控制的,请开放/websocket

前端页面:这里我使用了原生swagger+layer,请自行根据自己页面进行配置

'; 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; } $("#log-container div").append("

" + content.timestamp + " " + leverhtml + " --- [" + content.threadName + "] " + className + " :" + content.body + "

"); if (content.exception != "") { $("#log-container div").append("

" + content.exception + "

"); } if (content.cause != "") { $("#log-container div").append("

" + content.cause + "

"); } $("#log-container").scrollTop($("#log-container div").height() - $("#log-container").height()); }, { token: "kltoen" }); }); } } function closeSocket() { if (stompClient != null) { stompClient.disconnect(); stompClient = null; } }

最后来一个效果图(图中日志等级动态配置将在下篇博客说明)有什么不对的地方欢迎大牛们评论!!!


推荐阅读
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • 本文介绍了如何使用jQuery和AJAX来实现动态更新两个div的方法。通过调用PHP文件并返回JSON字符串,可以将不同的文本分别插入到两个div中,从而实现页面的动态更新。 ... [详细]
  • Activiti7流程定义开发笔记
    本文介绍了Activiti7流程定义的开发笔记,包括流程定义的概念、使用activiti-explorer和activiti-eclipse-designer进行建模的方式,以及生成流程图的方法。还介绍了流程定义部署的概念和步骤,包括将bpmn和png文件添加部署到activiti数据库中的方法,以及使用ZIP包进行部署的方式。同时还提到了activiti.cfg.xml文件的作用。 ... [详细]
  • Sleuth+zipkin链路追踪SpringCloud微服务的解决方案
    在庞大的微服务群中,随着业务扩展,微服务个数增多,系统调用链路复杂化。Sleuth+zipkin是解决SpringCloud微服务定位和追踪的方案。通过TraceId将不同服务调用的日志串联起来,实现请求链路跟踪。通过Feign调用和Request传递TraceId,将整个调用链路的服务日志归组合并,提供定位和追踪的功能。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了在Win10上安装WinPythonHadoop的详细步骤,包括安装Python环境、安装JDK8、安装pyspark、安装Hadoop和Spark、设置环境变量、下载winutils.exe等。同时提醒注意Hadoop版本与pyspark版本的一致性,并建议重启电脑以确保安装成功。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 服务器上的操作系统有哪些,如何选择适合的操作系统?
    本文介绍了服务器上常见的操作系统,包括系统盘镜像、数据盘镜像和整机镜像的数量。同时,还介绍了共享镜像的限制和使用方法。此外,还提供了关于华为云服务的帮助中心,其中包括产品简介、价格说明、购买指南、用户指南、API参考、最佳实践、常见问题和视频帮助等技术文档。对于裸金属服务器的远程登录,本文介绍了使用密钥对登录的方法,并提供了部分操作系统配置示例。最后,还提到了SUSE云耀云服务器的特点和快速搭建方法。 ... [详细]
  • 移动端常用单位——rem的使用方法和注意事项
    本文介绍了移动端常用的单位rem的使用方法和注意事项,包括px、%、em、vw、vh等其他常用单位的比较。同时还介绍了如何通过JS获取视口宽度并动态调整rem的值,以适应不同设备的屏幕大小。此外,还提到了rem目前在移动端的主流地位。 ... [详细]
  • XMLhttpREquest_Ajax技术总结之XmlHttpRequest
    Ajax1、 什么是ajax   ... [详细]
author-avatar
拍友2502908871
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有