输出日志HTML页面

427 阅读2分钟

描述

该方法是通过websocket连接方式,将filter过滤器收集到的日志信息保存到队列中。连接成功之后,将会获取到队列中的日志信息,发送给客户端,回显到页面上。

maven依赖

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.3</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
</dependency>

工具类

阻塞队列工具类

/**
 * 阻塞队列工具类
 *
 * @author: 苦瓜不苦
 * @date: 2022/6/26 1:24
 **/
public class LogQueueUtil {

    // 队列大小
    public final static int queue_max_size = 10000;
    // 阻塞队列
    private final static BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>(queue_max_size);


    /**
     * 消息入队
     *
     * @param message
     * @return
     */
    public static void push(String message) {
        // 队列满了就抛出异常,不阻塞
        blockingQueue.add(message);
    }

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

}

储存websocket实例工具类

/**
 * 储存websocket对象工具类
 *
 * @author: 苦瓜不苦
 * @date: 2022/6/26 1:31
 **/
@Slf4j
public class SessionUtil {

    // 储存Session实例对象
    private final static Map<String, Session> sessionMap = new ConcurrentHashMap<>();

    // 当前在线人数统计
    private final static AtomicInteger counter = new AtomicInteger();

    /**
     * 添加Session
     *
     * @param key
     * @param session
     */
    public synchronized static void put(String key, Session session) {
        sessionMap.put(key, session);
        log.info("ID {} 连接成功,当前在线人数 >>>>>> {}", key, counter.incrementAndGet());
    }

    /**
     * 移除并关闭Session
     *
     * @param key
     */
    public synchronized static void remove(String key) {
        try {
            Session session = sessionMap.remove(key);
            session.close();
            log.info("ID {} 关闭成功,当前在线人数 >>>>>> {}", key, counter.decrementAndGet());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 指定key获取Session
     *
     * @param key
     * @return
     */
    public static Session get(String key) {
        return sessionMap.get(key);
    }

    /**
     * 获取所有Session
     *
     * @return
     */
    public static Map<String, Session> getAll() {
        return sessionMap;
    }

}

日志filter过滤器

继承Filter<ILoggingEvent>类,重写decide方法。获取到日志信息,储存到队列中。

注意:将定义好的日志过滤类设置到logback.xml配置中。将<filter class="org.example.config.LogFilter"/>添加到<appender>标签里面。才能使定义好的类生效

/**
 * 日志收集过滤器
 *
 * @author: 苦瓜不苦
 * @date: 2022/6/26 1:49
 **/
public class LogFilter extends Filter<ILoggingEvent> {

    /**
     * 重新decide方法,获取到日志信息
     *
     * @param iLoggingEvent
     * @return
     */
    @Override
    public FilterReply decide(ILoggingEvent iLoggingEvent) {
        String message = StrUtil.strBuilder()
                .append("<pre>")
                .append(DateUtil.date(iLoggingEvent.getTimeStamp()))
                .append("\t")
                .append(iLoggingEvent.getLevel().levelStr)
                .append("\t")
                .append(iLoggingEvent.getLoggerName())
                .append("\t")
                .append(iLoggingEvent.getThreadName())
                .append("\t")
                .append(iLoggingEvent.getFormattedMessage())
                .append("<pre>")
                .toString();
        // 日志信息发送到队列中
        LogQueueUtil.push(message);
        return FilterReply.ACCEPT;
    }
}

websocket配置类

/**
 * websocket配置类
 *
 * @author: 苦瓜不苦
 * @date: 2022/6/26 1:39
 **/
@Configuration
public class WebsocketConfig {

    /**
     * 开启websocket注解模式
     * 会扫描带有@ServerEndpoint注解的类
     *
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }


    /**
     * 发送日志信息
     * 启动程序,该方法会自动执行
     */
    @PostConstruct
    public void sendLog() {
        // 开启多线程,避免阻塞程序启动
        ThreadUtil.execute(() -> {
            while (true) {
                // 获取阻塞队列中的消息,无消息时会进行阻塞
                String message = LogQueueUtil.poll();
                // 获取当前连接的websocket实例
                Map<String, Session> sessionMap = SessionUtil.getAll();
                sessionMap.forEach((key, session) -> {
                    // 发送消息
                    if (session.isOpen()) {
                        try {
                            session.getBasicRemote().sendText(message);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }

        });
    }

}

连接websocket类

/**
 * @author: 苦瓜不苦
 * @date: 2022/6/25 0:26
 **/
@Slf4j
@Component
@ServerEndpoint("/websocket")
public class WebsocketController {


    /**
     * 建立连接
     *
     * @param session
     */
    @OnOpen
    public void onOpen(Session session) {
        SessionUtil.put(session.getId(), session);
    }


    /**
     * 收到客户端消息后调用的方法
     *
     * @param session
     * @param message
     */
    @OnMessage
    public void onMessage(Session session, String message) {
        log.info("客户端发送的消息是 >>>>>> {}", message);
    }


    /**
     * 关闭连接
     *
     * @param session
     */
    @OnClose
    public void onClose(Session session) {
        SessionUtil.remove(session.getId());
    }

    /**
     * 异常方法
     *
     * @param session
     * @param throwable
     */
    @OnError
    public void onError(Session session, Throwable throwable) {
        log.error("错误原因 >>>>>> {}", throwable.getMessage());
    }

}

html页面

<!DOCTYPE HTML>
<html>
<head>
    <title>WebSocket日志打印</title>
</head>

<body>

    <input id="text" type="text" />
    <button onclick="send()">Send</button>
    <button onclick="closeWebSocket()">Close</button>
    <div id="message"></div>

</body>

<script type="text/javascript">

    let ws = null;
    //判断当前浏览器是否支持WebSocket
    if ('WebSocket' in window) {
        ws = new WebSocket("ws://localhost:29527/api/websocket");
    }
    else {
        alert('当前浏览器 Not support websocket')
    }

    //连接发生错误的回调方法
    ws.onerror = function () {
        setMessageInnerHTML("WebSocket连接发生错误");
    };

    //连接成功建立的回调方法
    ws.onopen = function(event) {
        console.log("ws调用连接成功回调方法")
    }
    //接收到消息的回调方法
    ws.onmessage = function(message) {
        if (typeof(message.data) == 'string') {
            setMessageInnerHTML(message.data);
        }
    }
    //ws连接断开的回调方法
    ws.onclose = function(e) {
        console.log("ws连接断开")
        //console.log(e)
        setMessageInnerHTML("ws close");
    }

    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').innerHTML += innerHTML;
    }

    //关闭连接
    function closeWebSocket() {
        ws.close();
    }


    //发送消息
    function send(msg) {
        if(!msg){
            msg = document.getElementById('text').value;
            document.getElementById('message').innerHTML += "发送的消息:" + msg + '<br/>';
            ws.send(msg);
        }
    }
	
</script>
</html>