Commit 8ef10799fb8f72d5c6255c27ce8f06aeac7d05f4

Authored by liwei2917
1 parent 7a101804

增加日志

@@ -50,6 +50,12 @@ @@ -50,6 +50,12 @@
50 </exclusion> 50 </exclusion>
51 </exclusions> 51 </exclusions>
52 </dependency> 52 </dependency>
  53 +
  54 + <!-- springboot websocket -->
  55 + <dependency>
  56 + <groupId>org.springframework.boot</groupId>
  57 + <artifactId>spring-boot-starter-websocket</artifactId>
  58 + </dependency>
53 59
54 <!-- 自动更新 --> 60 <!-- 自动更新 -->
55 <dependency> 61 <dependency>
src/main/java/com/cnlive/shenhe/ShenheApplication.java
@@ -5,13 +5,25 @@ import org.jasig.cas.client.session.SingleSignOutFilter; @@ -5,13 +5,25 @@ import org.jasig.cas.client.session.SingleSignOutFilter;
5 import org.jasig.cas.client.util.AssertionThreadLocalFilter; 5 import org.jasig.cas.client.util.AssertionThreadLocalFilter;
6 import org.jasig.cas.client.util.HttpServletRequestWrapperFilter; 6 import org.jasig.cas.client.util.HttpServletRequestWrapperFilter;
7 import org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter; 7 import org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter;
  8 +import org.slf4j.Logger;
  9 +import org.slf4j.LoggerFactory;
  10 +import org.springframework.beans.factory.annotation.Value;
  11 +import org.springframework.boot.ApplicationRunner;
8 import org.springframework.boot.SpringApplication; 12 import org.springframework.boot.SpringApplication;
9 import org.springframework.boot.autoconfigure.SpringBootApplication; 13 import org.springframework.boot.autoconfigure.SpringBootApplication;
10 import org.springframework.boot.web.servlet.FilterRegistrationBean; 14 import org.springframework.boot.web.servlet.FilterRegistrationBean;
11 import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 15 import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
12 import org.springframework.context.annotation.Bean; 16 import org.springframework.context.annotation.Bean;
  17 +import org.springframework.context.annotation.Configuration;
  18 +import org.springframework.stereotype.Controller;
  19 +import org.springframework.ui.Model;
  20 +import org.springframework.web.bind.annotation.GetMapping;
  21 +import org.springframework.web.servlet.ModelAndView;
  22 +
13 import tk.mybatis.spring.annotation.MapperScan; 23 import tk.mybatis.spring.annotation.MapperScan;
14 24
  25 +import java.net.InetAddress;
  26 +import java.net.UnknownHostException;
15 import java.util.HashMap; 27 import java.util.HashMap;
16 import java.util.Map; 28 import java.util.Map;
17 29
@@ -63,7 +75,7 @@ public class ShenheApplication extends SpringBootServletInitializer { @@ -63,7 +75,7 @@ public class ShenheApplication extends SpringBootServletInitializer {
63 Map<String,String> initParameters = new HashMap<String, String>(); 75 Map<String,String> initParameters = new HashMap<String, String>();
64 initParameters.put("casServerLoginUrl", CAS_SERVER_LOGIN_URL); 76 initParameters.put("casServerLoginUrl", CAS_SERVER_LOGIN_URL);
65 initParameters.put("service", SERVICE_LOGIN); 77 initParameters.put("service", SERVICE_LOGIN);
66 - initParameters.put("ignorePattern", "/api/*"); 78 + initParameters.put("ignorePattern", "/api/*|/logging/*");
67 registration.setInitParameters(initParameters); 79 registration.setInitParameters(initParameters);
68 // 设定加载的顺序 80 // 设定加载的顺序
69 registration.setOrder(2); 81 registration.setOrder(2);
@@ -249,4 +261,43 @@ public class ShenheApplication extends SpringBootServletInitializer { @@ -249,4 +261,43 @@ public class ShenheApplication extends SpringBootServletInitializer {
249 public static void main(String[] args) { 261 public static void main(String[] args) {
250 SpringApplication.run(ShenheApplication.class, args); 262 SpringApplication.run(ShenheApplication.class, args);
251 } 263 }
  264 +
  265 + /**
  266 + * 配置内部类
  267 + */
  268 + @Controller
  269 + @Configuration
  270 + class Config {
  271 +
  272 + /**
  273 + * 获取日志对象,构造函数传入当前类,查找日志方便定位
  274 + */
  275 + private final Logger logger = LoggerFactory.getLogger(this.getClass());
  276 +
  277 + @Value("${user.home}")
  278 + private String userName;
  279 +
  280 + /**
  281 + * 端口
  282 + */
  283 + @Value("${server.port}")
  284 + private String port;
  285 + /**
  286 + * 启动成功
  287 + */
  288 + @Bean
  289 + public ApplicationRunner applicationRunner() {
  290 + return applicationArguments -> {
  291 + try {
  292 + InetAddress ia = InetAddress.getLocalHost();
  293 + //获取本机内网IP
  294 + logger.info("启动成功:" + "http://" + ia.getHostAddress() + ":" + port + "/");
  295 + logger.info("${user.home} :" + userName);
  296 + } catch (UnknownHostException ex) {
  297 + ex.printStackTrace();
  298 + }
  299 + };
  300 + }
  301 +
  302 + }
252 } 303 }
src/main/java/com/cnlive/shenhe/api/AvsampleCallBackControllerApi.java
@@ -10,12 +10,15 @@ import com.cnlive.shenhe.utils.CommonUtils; @@ -10,12 +10,15 @@ import com.cnlive.shenhe.utils.CommonUtils;
10 import org.slf4j.Logger; 10 import org.slf4j.Logger;
11 import org.slf4j.LoggerFactory; 11 import org.slf4j.LoggerFactory;
12 import org.springframework.beans.factory.annotation.Autowired; 12 import org.springframework.beans.factory.annotation.Autowired;
  13 +import org.springframework.beans.factory.annotation.Value;
13 import org.springframework.stereotype.Controller; 14 import org.springframework.stereotype.Controller;
  15 +import org.springframework.ui.Model;
14 import org.springframework.web.bind.annotation.GetMapping; 16 import org.springframework.web.bind.annotation.GetMapping;
15 import org.springframework.web.bind.annotation.PostMapping; 17 import org.springframework.web.bind.annotation.PostMapping;
16 import org.springframework.web.bind.annotation.RequestBody; 18 import org.springframework.web.bind.annotation.RequestBody;
17 import org.springframework.web.bind.annotation.RequestMapping; 19 import org.springframework.web.bind.annotation.RequestMapping;
18 import org.springframework.web.bind.annotation.ResponseBody; 20 import org.springframework.web.bind.annotation.ResponseBody;
  21 +import org.springframework.web.servlet.ModelAndView;
19 22
20 import java.util.List; 23 import java.util.List;
21 24
@@ -73,4 +76,5 @@ public class AvsampleCallBackControllerApi { @@ -73,4 +76,5 @@ public class AvsampleCallBackControllerApi {
73 public String getGame(){ 76 public String getGame(){
74 return "html/game.html"; 77 return "html/game.html";
75 } 78 }
  79 +
76 } 80 }
src/main/java/com/cnlive/shenhe/logging/LoggingWSServer.java 0 → 100644
  1 +package com.cnlive.shenhe.logging;
  2 +
  3 +
  4 +import org.slf4j.Logger;
  5 +import org.slf4j.LoggerFactory;
  6 +import org.springframework.beans.factory.annotation.Value;
  7 +import org.springframework.stereotype.Component;
  8 +import org.thymeleaf.util.StringUtils;
  9 +
  10 +
  11 +import javax.websocket.*;
  12 +import javax.websocket.server.ServerEndpoint;
  13 +import java.io.BufferedReader;
  14 +import java.io.FileReader;
  15 +import java.io.IOException;
  16 +import java.text.SimpleDateFormat;
  17 +import java.util.Arrays;
  18 +import java.util.Date;
  19 +import java.util.Map;
  20 +import java.util.concurrent.ConcurrentHashMap;
  21 +
  22 +/**
  23 + * WebSocket获取实时日志并输出到Web页面
  24 + */
  25 +@Component
  26 +@ServerEndpoint(value = "/logging/websocket", configurator = MyEndpointConfigure.class)
  27 +public class LoggingWSServer {
  28 + private static Logger log = LoggerFactory.getLogger(LoggingWSServer.class);
  29 +
  30 + @Value("${spring.application.name}")
  31 + private String applicationName;
  32 +
  33 + /**
  34 + * 连接集合
  35 + */
  36 + private static Map<String, Session> sessionMap = new ConcurrentHashMap<String, Session>();
  37 + private static Map<String, Integer> lengthMap = new ConcurrentHashMap<String, Integer>();
  38 +
  39 + /**
  40 + * 连接建立成功调用的方法
  41 + */
  42 + @OnOpen
  43 + public void onOpen(Session session) {
  44 + //添加到集合中
  45 + sessionMap.put(session.getId(), session);
  46 + lengthMap.put(session.getId(), 1);//默认从第一行开始
  47 +
  48 + //获取日志信息
  49 + new Thread(() -> {
  50 + log.info("LoggingWebSocketServer 任务开始");
  51 + boolean first = true;
  52 + while (sessionMap.get(session.getId()) != null) {
  53 + BufferedReader reader = null;
  54 + try {
  55 + //日志文件路径,获取最新的
  56 + String filePath = System.getProperty("user.home") + "/log/" + new SimpleDateFormat("yyyyMMdd").format(new Date()) + "/"+applicationName+".log";
  57 +
  58 + //字符流
  59 + reader = new BufferedReader(new FileReader(filePath));
  60 + Object[] lines = reader.lines().toArray();
  61 +
  62 + //只取从上次之后产生的日志
  63 + Object[] copyOfRange = Arrays.copyOfRange(lines, lengthMap.get(session.getId()), lines.length);
  64 +
  65 + //对日志进行着色,更加美观 PS:注意,这里要根据日志生成规则来操作
  66 + for (int i = 0; i < copyOfRange.length; i++) {
  67 + String line = (String) copyOfRange[i];
  68 + //先转义
  69 + line = line.replaceAll("&", "&amp;")
  70 + .replaceAll("<", "&lt;")
  71 + .replaceAll(">", "&gt;")
  72 + .replaceAll("\"", "&quot;");
  73 +
  74 + //处理等级
  75 + line = line.replace("DEBUG", "<span style='color: blue;'>DEBUG</span>");
  76 + line = line.replace("INFO", "<span style='color: green;'>INFO</span>");
  77 + line = line.replace("WARN", "<span style='color: orange;'>WARN</span>");
  78 + line = line.replace("ERROR", "<span style='color: red;'>ERROR</span>");
  79 +
  80 + //处理类名
  81 + String[] split = line.split("]");
  82 + if (split.length >= 2) {
  83 + String[] split1 = split[1].split("-");
  84 + if (split1.length >= 2) {
  85 + line = split[0] + "]" + "<span style='color: #298a8a;'>" + split1[0] + "</span>" + "-" + split1[1];
  86 + }
  87 + }
  88 +
  89 + copyOfRange[i] = line;
  90 + }
  91 +
  92 + //存储最新一行开始
  93 + lengthMap.put(session.getId(), lines.length);
  94 +
  95 + //第一次如果太大,截取最新的200行就够了,避免传输的数据太大
  96 + if(first && copyOfRange.length > 200){
  97 + copyOfRange = Arrays.copyOfRange(copyOfRange, copyOfRange.length - 200, copyOfRange.length);
  98 + first = false;
  99 + }
  100 +
  101 + String result = StringUtils.join(copyOfRange, "<br/>");
  102 +
  103 + //发送
  104 + send(session, result);
  105 +
  106 + //休眠一秒
  107 + Thread.sleep(1000);
  108 + } catch (Exception e) {
  109 + //捕获但不处理
  110 + e.printStackTrace();
  111 + } finally {
  112 + try {
  113 + reader.close();
  114 + } catch (IOException ignored) {
  115 + }
  116 + }
  117 + }
  118 + log.info("LoggingWebSocketServer 任务结束");
  119 + }).start();
  120 + }
  121 +
  122 + /**
  123 + * 连接关闭调用的方法
  124 + */
  125 + @OnClose
  126 + public void onClose(Session session) {
  127 + //从集合中删除
  128 + sessionMap.remove(session.getId());
  129 + lengthMap.remove(session.getId());
  130 + }
  131 +
  132 + /**
  133 + * 发生错误时调用
  134 + */
  135 + @OnError
  136 + public void onError(Session session, Throwable error) {
  137 + error.printStackTrace();
  138 + }
  139 +
  140 + /**
  141 + * 服务器接收到客户端消息时调用的方法
  142 + */
  143 + @OnMessage
  144 + public void onMessage(String message, Session session) {
  145 +
  146 + }
  147 +
  148 + /**
  149 + * 封装一个send方法,发送消息到前端
  150 + */
  151 + private void send(Session session, String message) {
  152 + try {
  153 + session.getBasicRemote().sendText(message);
  154 + } catch (Exception e) {
  155 + e.printStackTrace();
  156 + }
  157 + }
  158 +}
src/main/java/com/cnlive/shenhe/logging/MyEndpointConfigure.java 0 → 100644
  1 +package com.cnlive.shenhe.logging;
  2 +
  3 +import org.springframework.beans.BeansException;
  4 +import org.springframework.beans.factory.BeanFactory;
  5 +import org.springframework.context.ApplicationContext;
  6 +import org.springframework.context.ApplicationContextAware;
  7 +
  8 +import javax.websocket.server.ServerEndpointConfig;
  9 +
  10 +/**
  11 + * 解决注入其他类的问题,详情参考这篇帖子:webSocket无法注入其他类:https://blog.csdn.net/tornadojava/article/details/78781474
  12 + */
  13 +public class MyEndpointConfigure extends ServerEndpointConfig.Configurator implements ApplicationContextAware {
  14 +
  15 + private static volatile BeanFactory context;
  16 +
  17 + @Override
  18 + public <T> T getEndpointInstance(Class<T> clazz){
  19 + return context.getBean(clazz);
  20 + }
  21 +
  22 + @Override
  23 + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  24 + MyEndpointConfigure.context = applicationContext;
  25 + }
  26 +}
src/main/java/com/cnlive/shenhe/logging/WebSocketConfig.java 0 → 100644
  1 +package com.cnlive.shenhe.logging;
  2 +
  3 +import org.springframework.context.annotation.Bean;
  4 +import org.springframework.context.annotation.Configuration;
  5 +import org.springframework.web.socket.server.standard.ServerEndpointExporter;
  6 +
  7 +/**
  8 + * WebSocket配置
  9 + */
  10 +@Configuration
  11 +public class WebSocketConfig {
  12 +
  13 +
  14 + /**
  15 + * 用途:扫描并注册所有携带@ServerEndpoint注解的实例。 @ServerEndpoint("/websocket")
  16 + * PS:如果使用外部容器 则无需提供ServerEndpointExporter。
  17 + */
  18 + @Bean
  19 + public ServerEndpointExporter serverEndpointExporter() {
  20 + return new ServerEndpointExporter();
  21 + }
  22 +
  23 + /**
  24 + * 支持注入其他类
  25 + */
  26 + @Bean
  27 + public MyEndpointConfigure newMyEndpointConfigure (){
  28 + return new MyEndpointConfigure ();
  29 + }
  30 +}
src/main/java/com/cnlive/shenhe/logging/logging.java 0 → 100644
  1 +package com.cnlive.shenhe.logging;
  2 +
  3 +import org.springframework.beans.factory.annotation.Value;
  4 +import org.springframework.stereotype.Controller;
  5 +import org.springframework.ui.Model;
  6 +import org.springframework.web.bind.annotation.GetMapping;
  7 +import org.springframework.web.bind.annotation.RequestMapping;
  8 +import org.springframework.web.servlet.ModelAndView;
  9 +
  10 +
  11 +/**
  12 + * @Auther: 李伟
  13 + * @Date: 2019/08/13 10:43
  14 + * @Description:log实时日志
  15 + */
  16 +@Controller
  17 +@RequestMapping("/logging")
  18 +public class logging {
  19 + /**
  20 + * 端口
  21 + */
  22 + @Value("${server.port}")
  23 + private String port;
  24 +
  25 + /**
  26 + * 跳转实时日志
  27 + */
  28 + @GetMapping("/")
  29 + public ModelAndView logging( Model model) {
  30 + model.addAttribute("port", port);
  31 + return new ModelAndView("logging/logging.html");
  32 + }
  33 +}
src/main/java/com/cnlive/shenhe/utils/AntiFraudUtil.java
@@ -42,7 +42,7 @@ public class AntiFraudUtil { @@ -42,7 +42,7 @@ public class AntiFraudUtil {
42 // String btId = uuid.toString(); 42 // String btId = uuid.toString();
43 43
44 // System.out.println("文本:"+AntiFraudUtil.filterText("0000001", "王八蛋", "DYNAMIC_USER") + "---"); 44 // System.out.println("文本:"+AntiFraudUtil.filterText("0000001", "王八蛋", "DYNAMIC_USER") + "---");
45 -// System.out.println(AntiFraudUtil.filterArticle("000111","http://3g.huabian.com/api/hz0227/show/388865.html",null)); 45 +// System.out.println(AntiFraudUtil.filterArticle("000111","http://shineup.china.com.cn/WJJ/detail2_2019_08/13/1365957.html",null));
46 //System.out.println("图片:"+AntiFraudUtil.filterImg("0000001", jsonArray,"DYNAMIC_BUSINESS") + "---"); 46 //System.out.println("图片:"+AntiFraudUtil.filterImg("0000001", jsonArray,"DYNAMIC_BUSINESS") + "---");
47 //System.out.println("视频:"+AntiFraudUtil.filterVideo("http://wjj.ys1.cnliveimg.com/testVideo/test1.mp4",btId,null) + "---"); 47 //System.out.println("视频:"+AntiFraudUtil.filterVideo("http://wjj.ys1.cnliveimg.com/testVideo/test1.mp4",btId,null) + "---");
48 //queryVideoResult("02b649b7-3451-403e-abd0-592302cec2a0"); 48 //queryVideoResult("02b649b7-3451-403e-abd0-592302cec2a0");
src/main/resources/application.properties
@@ -36,6 +36,8 @@ mybatis.mapper-locations=classpath:mapping/*.xml @@ -36,6 +36,8 @@ mybatis.mapper-locations=classpath:mapping/*.xml
36 mybatis.type-aliases-package=com.cnlive.shenhe.entity 36 mybatis.type-aliases-package=com.cnlive.shenhe.entity
37 server.port=82 37 server.port=82
38 38
  39 +spring.application.name=cnlive_shenhe
  40 +
39 sync_users_api=http://api.cnlive.com/open/api2/inner2/shenhe/getuser 41 sync_users_api=http://api.cnlive.com/open/api2/inner2/shenhe/getuser
40 sync_users_app_key=314a9dd4-a9b7-4c12-bbbe-1150a36491ae 42 sync_users_app_key=314a9dd4-a9b7-4c12-bbbe-1150a36491ae
41 43
src/main/resources/logback-spring.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!--
  3 +scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
  4 +scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
  5 +debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
  6 +-->
  7 +<configuration scan="false" scanPeriod="60 seconds" debug="false">
  8 + <!--日志文件主目录:这里${user.home}为当前服务器用户主目录-->
  9 + <property name="LOG_HOME" value="${user.home}/log" />
  10 + <!-- 定义日志文件名称 -->
  11 +<!-- <property name="appName" value="appName" source="spring.application.name"></property> -->
  12 + <!--日志文件名称:这里spring.application.name表示工程名称-->
  13 + <springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
  14 + <!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
  15 + <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
  16 + <!--
  17 + 日志输出格式:
  18 + %d表示日期时间,
  19 + %thread表示线程名,
  20 + %-5level:级别从左显示5个字符宽度
  21 + %logger{50} 表示logger名字最长50个字符,否则按照句点分割。
  22 + %msg:日志消息,
  23 + %n是换行符
  24 + -->
  25 + <layout class="ch.qos.logback.classic.PatternLayout">
  26 + <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
  27 + </layout>
  28 + </appender>
  29 +
  30 + <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
  31 + <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
  32 + <!-- 指定日志文件的名称 -->
  33 +<!-- <file>${LOG_HOME}/%d{yyyyMMdd}/${APP_NAME}.log</file> -->
  34 + <!--
  35 + 当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名
  36 + TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。
  37 + -->
  38 + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  39 + <!--
  40 + 滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动
  41 + %i:当文件大小超过maxFileSize时,按照i进行文件滚动
  42 + -->
  43 + <fileNamePattern>${LOG_HOME}/%d{yyyyMMdd}/${APP_NAME}.log</fileNamePattern>
  44 + <!--
  45 + 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动,
  46 + 且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是,
  47 + 那些为了归档而创建的目录也会被删除。
  48 + -->
  49 + <MaxHistory>30</MaxHistory>
  50 + <!--
  51 + 当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy
  52 + -->
  53 +<!-- <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> -->
  54 +<!-- <maxFileSize>100MB</maxFileSize> -->
  55 +<!-- </timeBasedFileNamingAndTriggeringPolicy> -->
  56 + </rollingPolicy>
  57 + <!-- 日志输出格式: -->
  58 + <layout class="ch.qos.logback.classic.PatternLayout">
  59 + <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
  60 + </layout>
  61 + </appender>
  62 +
  63 + <!--
  64 + logger主要用于存放日志对象,也可以定义日志类型、级别
  65 + name:表示匹配的logger类型前缀,也就是包的前半部分
  66 + level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR
  67 + additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出,
  68 + false:表示只用当前logger的appender-ref,true:
  69 + 表示当前logger的appender-ref和rootLogger的appender-ref都有效
  70 + -->
  71 + <!-- hibernate logger -->
  72 + <logger name="com.yuanqinnan" level="debug" />
  73 + <!-- Spring framework logger -->
  74 + <logger name="org.springframework" level="debug" additivity="false"></logger>
  75 +
  76 +
  77 +
  78 + <!--
  79 + root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,
  80 + 要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。
  81 + -->
  82 + <root level="info">
  83 + <appender-ref ref="stdout" />
  84 + <appender-ref ref="appLogAppender" />
  85 + </root>
  86 +</configuration>
0 \ No newline at end of file 87 \ No newline at end of file
src/main/resources/templates/logging/logging.html 0 → 100644
  1 +<!DOCTYPE>
  2 +<!--解决idea thymeleaf 表达式模板报红波浪线-->
  3 +<!--suppress ALL -->
  4 +<html xmlns:th="http://www.thymeleaf.org">
  5 +<head>
  6 + <meta charset="UTF-8">
  7 + <title>实时日志</title>
  8 + <!-- jquery -->
  9 + <script th:src="@{../../static/js/jquery.min.js}"></script>
  10 +<!-- <script src="../../static/js/jquery-1.9.1.min.js"></script> -->
  11 +</head>
  12 +<body>
  13 +<!-- 标题 -->
  14 +<h1 style="text-align: center;">实时日志</h1>
  15 +
  16 +<!-- 显示区 -->
  17 +<div id="loggingText" contenteditable="true"
  18 + style="width:100%;height: 600px;background-color: ghostwhite; overflow: auto;"></div>
  19 +
  20 +<!-- 操作栏 -->
  21 +<div style="text-align: center;">
  22 + <button onclick="$('#loggingText').text('')" style="color: green; height: 35px;">清屏</button>
  23 + <button onclick="$('#loggingText').animate({scrollTop:$('#loggingText')[0].scrollHeight});"
  24 + style="color: green; height: 35px;">滚动至底部
  25 + </button>
  26 + <button onclick="if(window.loggingAutoBottom){$(this).text('开启自动滚动');}else{$(this).text('关闭自动滚动');};window.loggingAutoBottom = !window.loggingAutoBottom"
  27 + style="color: green; height: 35px; ">开启自动滚动
  28 + </button>
  29 +</div>
  30 +</body>
  31 +<script th:inline="javascript">
  32 + //websocket对象
  33 + let websocket = null;
  34 + var re=location.origin;
  35 + console.log(re);
  36 + //判断当前浏览器是否支持WebSocket
  37 + if ('WebSocket' in window) {
  38 + websocket = new WebSocket("ws://localhost:"+[[${port}]]+"/logging/websocket");
  39 + } else {
  40 + console.error("不支持WebSocket");
  41 + }
  42 +
  43 + //连接发生错误的回调方法
  44 + websocket.onerror = function (e) {
  45 + console.error("WebSocket连接发生错误");
  46 + };
  47 +
  48 + //连接成功建立的回调方法
  49 + websocket.onopen = function () {
  50 + console.log("WebSocket连接成功")
  51 + };
  52 +
  53 + //接收到消息的回调方法
  54 + websocket.onmessage = function (event) {
  55 + //追加
  56 + if (event.data) {
  57 +
  58 + //日志内容
  59 + let $loggingText = $("#loggingText");
  60 + $loggingText.append(event.data);
  61 +
  62 + //是否开启自动底部
  63 + if (window.loggingAutoBottom) {
  64 + //滚动条自动到最底部
  65 + $loggingText.scrollTop($loggingText[0].scrollHeight);
  66 + }
  67 + }
  68 + }
  69 +
  70 + //连接关闭的回调方法
  71 + websocket.onclose = function () {
  72 + console.log("WebSocket连接关闭")
  73 + };
  74 +</script>
  75 +</html>
0 \ No newline at end of file 76 \ No newline at end of file