最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

websocket實(shí)現(xiàn)方法日志實(shí)時(shí)查詢過程

 更新時(shí)間:2025年07月21日 15:02:29   作者:老葉童鞋  
本文介紹了一種基于Redis生成唯一key的方案,前端傳遞key至服務(wù)器,服務(wù)器據(jù)此生成日志文件并通過WebSocket線程實(shí)時(shí)讀取返回內(nèi)容,結(jié)合logback實(shí)現(xiàn)日志記錄與監(jiān)控

本次方法的核心概念是通過redis生成唯一key值(沒有放出來),然后通過前端獲取這個(gè)唯一的key帶入到方法請(qǐng)求中,然后服務(wù)器通過這個(gè)key生成此次方法生成唯一的日志文件,websocket接口通過線程實(shí)時(shí)讀取key文件返回內(nèi)容。

1、通過logback生成日志工具類

package utils;

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
import ch.qos.logback.core.util.FileSize;
import ch.qos.logback.core.util.OptionHelper;
import cn.sunline.dds.common.framework.constants.SocketLog;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;

/**
 * 日志構(gòu)建器
 * @author Mr.Ye
 * @Description: 該方法不會(huì)在控制臺(tái)打印日志,只用于生成指定文件的日志
 */
public class LoggerBuilder {  
  
    private static final Map<String,Logger> container = new HashMap<>();

    private static SocketLog socketLog;

    public static Logger getLogger(String key) {
        Logger logger = container.get(key);
        if(logger != null) {
            return logger;
        }
        synchronized (LoggerBuilder.class) {
            logger = container.get(key);
            if(logger != null) {
                return logger;
            }
            logger = build(key);
            container.put(key,logger);
        }
        return logger;
    }
    //刪除container中的log
    public static void close(String key) {
        if (container.containsKey(key)) {
            container.remove(key);
        }
    }
  
    private static Logger build(String key) {
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();  
        Logger logger = context.getLogger("FILELOGGER");
        logger.setAdditive(false);  
        RollingFileAppender appender = new RollingFileAppender();  
        appender.setContext(context);  
        appender.setName("FILELOGGER");
        appender.setFile(OptionHelper.substVars("/logs/"+ key + ".log",context));
        appender.setAppend(true);  
        appender.setPrudent(false);
        //重命名日志文件
        SizeAndTimeBasedRollingPolicy policy = new SizeAndTimeBasedRollingPolicy();
        String fp = OptionHelper.substVars("/logs/" + key + ".log.%d{yyyy-MM-dd}.%i",context);
        policy.setMaxFileSize(FileSize.valueOf("128MB"));
        policy.setFileNamePattern(fp);
        policy.setMaxHistory(7);
        policy.setTotalSizeCap(FileSize.valueOf("32GB"));
        policy.setParent(appender);
        policy.setContext(context);
        policy.start();
  
        PatternLayoutEncoder encoder = new PatternLayoutEncoder();  
        encoder.setContext(context);  
        encoder.setPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n");
        encoder.start();  
  
        appender.setRollingPolicy(policy);  
        appender.setEncoder(encoder);  
        appender.start();  
        logger.addAppender(appender);  
        return logger;  
    }
}

2、構(gòu)建websocket方法

package socket;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Data: 2022/04/22
 * @author Mr.Ye
 * @deprecated 由于讀寫文件占用資源較大,所以暫時(shí)不使用
 */
@ServerEndpoint("/log/{key}")
@Component
public class LogWebSocket {
    private final Logger logger = LoggerFactory.getLogger(LogWebSocket.class);

    /**
     * 記錄當(dāng)前總共有多少個(gè)連接
     */
    private static final AtomicInteger ONLINE_COUNT = new AtomicInteger(0);
    /**
     * 項(xiàng)目路徑
     */
    private static final String PROPERTY = System.getProperty("user.dir");

    /**
     * 存放當(dāng)前正在做靈活開發(fā)的session對(duì)象
     */
    private static final Map<String, Session> CLIENTS = new ConcurrentHashMap<>();

    /**
     * 新的WebSocket請(qǐng)求開啟
     * @describe 當(dāng)前方法各系統(tǒng)通用,通過file獲取日志文件,linux下可以使用tail -f持續(xù)獲取日志文件更方便
     */
    @OnOpen
    public void onOpen(Session session,@PathParam("key")String key) {
        try {
            System.out.println("------------------key------------------:"+key);
            // 建立連接梳理 加 1
            ONLINE_COUNT.incrementAndGet();
            // 將當(dāng)前創(chuàng)建的session 存儲(chǔ)起來
            CLIENTS.put(session.getId(), session);
            logger.info("有新窗口打開連接加入:{},當(dāng)前正在查詢總數(shù)為:{}", session.getId(), ONLINE_COUNT.get());
            String url = PROPERTY+ "/logs/"+key+".log";
            LogFileTailer tailer = new LogFileTailer(url);
            tailer.addListener(log -> {
                try {
//                    session.getBasicRemote().sendText(log + "<br />");
                    session.getBasicRemote().sendText(log);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            tailer.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    /**
     * WebSocket請(qǐng)求關(guān)閉
     */
    @OnClose
    public void onClose(Session session) {
        // 建立連接梳理 減 1
        ONLINE_COUNT.decrementAndGet();
        // 刪除緩存起來的session
        CLIENTS.remove(session.getId());
        logger.info("有一窗口連接關(guān)閉:{},當(dāng)前正在開發(fā)總數(shù)為:{}", session.getId(), ONLINE_COUNT.get());
    }

    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
        // 建立連接梳理 減 1
        ONLINE_COUNT.decrementAndGet();
        // 刪除緩存起來的session
        CLIENTS.remove(session.getId());
        logger.error("發(fā)生錯(cuò)誤,刪除當(dāng)前session:{},剩余正在總數(shù)為:{}", session.getId(), ONLINE_COUNT.get());
    }
}

3、日志監(jiān)聽獲取

package socket;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.function.Consumer;

/**
 * @Data: 2022/04/24
 * @author Mr.Ye
 * @deprecated 由于讀寫文件占用資源較大,所以暫時(shí)不使用
 */
public class LogFileTailer extends Thread {

	private File logfile;

	private Consumer<String> callback;

	/**
	 * 監(jiān)視開關(guān),true = 打開監(jiān)視
	 */
	private boolean tailing = true;

	/**
	 * 
	 * @param file 要監(jiān)視的文本文件
	 */
	public LogFileTailer(String file) {
		logfile = new File(file);

	}

	/**
	 *Tailing 開關(guān)
	 * @param tailing
	 */
	public void Tailing(boolean tailing) {
		this.tailing = tailing;

	}

	/**
	 * 設(shè)置回調(diào)事件
	 * 
	 * @param callback 回調(diào)事件
	 */
	public void addListener(Consumer<String> callback) {
		this.callback = callback;
	}

	@Override
	public void run() {
		/*上一次讀取文件位置*/
		long filePointer =0;
		try {
			RandomAccessFile file  = new RandomAccessFile(logfile, "r");
			while (tailing) {
				long fileLength = logfile.length();
				if (fileLength < filePointer) {
					file = new RandomAccessFile(logfile, "r");
					filePointer = 0;
				}
				if (fileLength > filePointer) {
					file.seek(filePointer);
					String line = file.readLine();

					while (line != null) {
						line = new String(line.getBytes("ISO-8859-1"), "utf-8");
						if (callback != null){
							callback.accept(line);
						}
						line = file.readLine();
					}

					filePointer = file.getFilePointer();
				}

//				sleep(sampleInterval);
			}

			file.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

4、問題補(bǔ)充

SpringBoot中需要注入bean,通過注入一個(gè)ServerEndpointExporter,該Bean會(huì)自動(dòng)注冊(cè)使用@ServerEndpoint注解申明的websocket endpoint。

要注意:如果使用獨(dú)立的servlet容器,而不是直接使用springboot的內(nèi)置容器,

就不要注入ServerEndpointExporter,因?yàn)樗鼘⒂扇萜髯约禾峁┖凸芾?,所以在注?/p>

ServerEndpointExporter時(shí)需要增加一些配置

package conf;

import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;

/**
 * 注入一個(gè)ServerEndpointExporter,
 * 該Bean會(huì)自動(dòng)注冊(cè)使用@ServerEndpoint注解申明的websocket endpoint
 * @author Mr.Ye
 *
 */
@Configuration
public class WebSocketConfig implements ServletContextInitializer {

    /**
     * 要注意,如果使用獨(dú)立的servlet容器,而不是直接使用springboot的內(nèi)置容器,
     * 就不要注入ServerEndpointExporter,因?yàn)樗鼘⒂扇萜髯约禾峁┖凸芾?
     * 所以增加以下配置:
     * @return
     */
	@Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        servletContext.setInitParameter("org.apache.tomcat.websocket.textBufferSize", String.valueOf(10 * 1024 * 1024));
        servletContext.setInitParameter("org.apache.tomcat.websocket.binaryBufferSize", String.valueOf(10 * 1024 * 1024));
    }
}

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 深入剖析Java中Map.Entry的方法與實(shí)戰(zhàn)應(yīng)用

    深入剖析Java中Map.Entry的方法與實(shí)戰(zhàn)應(yīng)用

    在Java集合框架中,Map.Entry扮演著連接鍵值對(duì)的橋梁角色,作為Map接口的內(nèi)部接口,它封裝了鍵值對(duì)的本質(zhì),是高效處理映射數(shù)據(jù)的核心工具,下面我我們就來深入剖析Map.Entry的概念,方法及實(shí)戰(zhàn)應(yīng)用
    2025-06-06
  • Java使用poi-tl1.9.1生成Word文檔的技巧分享

    Java使用poi-tl1.9.1生成Word文檔的技巧分享

    本文將簡(jiǎn)單介紹poi-tl的相關(guān)知識(shí),通過一個(gè)實(shí)際的案例實(shí)踐,充分介紹如何利用poi-tl進(jìn)行目標(biāo)文檔的生成,同時(shí)分享幾個(gè)不同的office版本如何進(jìn)行圖表生成的解決方案,需要的朋友可以參考下
    2023-09-09
  • Java中的StackOverflowError錯(cuò)誤問題及解決方法

    Java中的StackOverflowError錯(cuò)誤問題及解決方法

    這篇文章主要介紹了Java中的StackOverflowError錯(cuò)誤,在本文中,我們仔細(xì)研究了StackOverflower錯(cuò)誤,包括Java代碼如何導(dǎo)致它,以及我們?nèi)绾卧\斷和修復(fù)它,需要的朋友可以參考下
    2022-07-07
  • JAVA簡(jiǎn)單鏈接Oracle數(shù)據(jù)庫(kù) 注冊(cè)和登陸功能的實(shí)現(xiàn)代碼

    JAVA簡(jiǎn)單鏈接Oracle數(shù)據(jù)庫(kù) 注冊(cè)和登陸功能的實(shí)現(xiàn)代碼

    這篇文章主要介紹了JAVA鏈接Oracle并實(shí)現(xiàn)注冊(cè)與登錄功能的代碼實(shí)例,有需要的朋友可以參考一下
    2014-01-01
  • Java猜數(shù)字小游戲?qū)崿F(xiàn)辦法與詳解

    Java猜數(shù)字小游戲?qū)崿F(xiàn)辦法與詳解

    Java猜數(shù)字游戲是一款簡(jiǎn)單的命令行游戲,玩家需要在1到100之間猜測(cè)一個(gè)由計(jì)算機(jī)隨機(jī)生成的數(shù)字,這篇文章主要介紹了Java猜數(shù)字小游戲?qū)崿F(xiàn)辦法與詳解的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-11-11
  • Java應(yīng)該在哪里判斷List是否為空

    Java應(yīng)該在哪里判斷List是否為空

    在Java中,我們常用List來存儲(chǔ)數(shù)據(jù),但是我們?cè)趺磁袛嗨欠癯晒砹宋覀冃枰臄?shù)據(jù)呢?下面這篇文章主要給大家介紹了關(guān)于Java應(yīng)該在哪里判斷List是否為空的相關(guān)資料,需要的朋友可以參考下
    2022-02-02
  • Spring AOP的幾種實(shí)現(xiàn)方式總結(jié)

    Spring AOP的幾種實(shí)現(xiàn)方式總結(jié)

    本篇文章主要介紹了Spring AOP的幾種實(shí)現(xiàn)方式總結(jié),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-02-02
  • java報(bào)錯(cuò)非法的前向引用問題

    java報(bào)錯(cuò)非法的前向引用問題

    這篇文章主要介紹了java報(bào)錯(cuò)非法的前向引用問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • Java ConcurrentModificationException異常解決案例詳解

    Java ConcurrentModificationException異常解決案例詳解

    這篇文章主要介紹了Java ConcurrentModificationException異常解決案例詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • Java中BigDecimal類的使用詳解

    Java中BigDecimal類的使用詳解

    這篇文章主要介紹了Java中BigDecimal類的使用詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07

最新評(píng)論

通江县| 仙桃市| 崇仁县| 旅游| 珲春市| 福建省| 平潭县| 泗洪县| 佛山市| 抚顺县| 呼伦贝尔市| 鄂托克前旗| 容城县| 格尔木市| 赤峰市| 龙里县| 南城县| 富源县| 罗源县| 望谟县| 蒙山县| 贵溪市| 河津市| 蒙阴县| 安西县| 卫辉市| 绵阳市| 阜新| 松潘县| 竹北市| 武平县| 久治县| 富宁县| 阳曲县| 两当县| 凌海市| 南平市| 南陵县| 永安市| 十堰市| 巴林左旗|