摘要

本期首要以规矩引擎业务完成为背景,介绍长任务运行日志如何在页面端持续显现更新,用最小的消耗跟踪复杂逻辑的履行情况以便于问题定位

作用

换不多说先上作用,紧接着跟进完成及处理逻辑

聊聊长日志持续输出

完成思路及组件

长任务当然优先是websocket比较好用,如果用轮询遍历的方法完成也非不行,无非入库查询或缓存,但之所以没用这种,首要考虑存在添加服务器附加代码处理,不利于剥离、添加额外的客户端与服务器taceId联动逻辑处理,且追寻交互作用不够好、

  • 前端,介绍显现日志首要选用react技术栈,组件为 “console-feed”,相同的绑架console.log是基操,但我这里更垂青的是在日志输出的一起能够输出目标“像console目标输出相同”,,别的一个不幸便是深度2级以上的目标无法正常输出,别的一个便是能够自己完成一个console函数相同能够达到意图。
npm i console-feed

首要页面逻辑如下,分别为日志完成、WebSocket连接、日志输出格式预处理

const Log={};
Log.logs = [];
['log', 'dir','table','group','warn', 'info', 'error', 'debug', 'assert', 'time', 'timeEnd'].forEach(
  method => {
    Log[`$${method}`] = Log[method]
    Log[method] = (mes) => {
    //  Log.logs.push(mes)
    }
  }
)
function createWebSocket(options:object){
    options = options || {};
    var socket;
    socket = new WebSocket(options.url ||(location.origin.replace("http",'ws') + '/ws'));
    socket.onopen = options.onopen.bind(this);
    socket.onmessage = options.onmessage.bind(this);
    socket.onerror = options.onerror || function(){
    //	layer.layer.msg('WebSocket过错');
      console.warn('WebSocket过错')
    }
    return socket;
  }
function debounce(fn, delay) {
    let time = null;//time用来操控事件的触发
    return function () {
        if (time !== null) {
            clearTimeout(time);
        }
        time = setTimeout(() => {
            fn.call(this);
            //利用call(),让this的指针从指向window 转成指向input
        }, delay)
    }
}
const ip=location.host;
    socket =createWebSocket({
       url:`ws://${ip}/ws`,
      onopen : function(){
        const flow=this.toJsonFlow();
        socket.send(JSON.stringify({
          eventType : 'debug',
          message : JSON.stringify(flow)//editor.getXML()
        }));
      },
      onmessage : function(e){
        var event = JSON.parse(e.data);
        var eventType = event.eventType;
        var message = event.message;
        const pushMessage=(event,type)=>{
            var item=event.message;
            switch(type){
              case "log":
                Log[item.level](`${event.timestamp}:\n${item.message}`);
                break;
              case "output":
                Log.table( JSON.stringify(item));
                break;
                default:
                  Log.info(type);
                  break
            }
            if(eventType=="finish"){
              this.running$.next(false);
            }
        }
        const eventHandle={
          'finish':pushMessage,
          'output':pushMessage,
          'log':pushMessage,
          'debug':pushMessage,
        }
      const handle= eventHandle[eventType];
      // if(handle)handle(event,eventType);
      debounce(function () {
        if(handle)handle(event,eventType);
    }, 200)();

react监听处理逻辑

import { Console, Hook, Unhook } from 'console-feed'
useEffect(() => {
 Hook(Log, log =>{
    setLogs(currLogs =>[...currLogs, log])
    document.querySelector(".consoleLog").scrollIntoView({behavior: "smooth", block: "end"});
  }, false,1000)
  })
  return () => Unhook(Log)
  }, [])

react渲染

 <Console logs={logs} variant="dark"  />
  • 服务端首要思路是借用日志输出组件,经过重写日志append领先线程上下文做筛选判别,选择性的格式化日志输出内容入websocket中,达到即不损坏原逻辑,又可遴选记载日志内容的意图。
    日志记载,原本该怎样记载日志就怎样记载
private static Logger logger = LoggerFactory.getLogger(Flower.class);

日志阻拦

public class LogWebSocketAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {
	@Override
	protected void append(ILoggingEvent event) {
		LogContext context = LogContextContextHolder.get();
		if(context instanceof LogWebSocketContext){
			LogWebSocketContext socketContext = (LogWebSocketContext) context;
			Object[] argumentArray = event.getArgumentArray();
			List<Object> arguments = argumentArray == null ? Collections.emptyList()  : new ArrayList<>(Arrays.asList(argumentArray));
			ThrowableProxy throwableProxy = (ThrowableProxy) event.getThrowableProxy();
			if(throwableProxy != null){
				arguments.add(throwableProxy.getThrowable());
			}
			socketContext.log(new LogFmt(event.getLevel().levelStr.toLowerCase(),event.getFormattedMessage(),arguments));
		}
	}
}

websocket 接收启动信号,并输出相关日志,能够看到,关键的环节处的逻辑已经达到了剥离和格式化的意图

	private static Logger logger = LoggerFactory.getLogger(Flower.class);
    @OnMessage
    public void onMessage(String message, Session session) {
        JSONObject event = JSON.parseObject(message);
            context = new LogWebSocketContext(session);
            //履行首要逻辑代码,相关在该线程下履行的日志会得以记载输出
            }

补充

本着配套送佛送到西的情绪,nginx websocket装备支撑如下

$connection_upgrade
    map $http_upgrade $connection_upgrade { 
        default          keep-alive;  #默认为keep-alive 能够支撑 一般http恳求
        'websocket'      upgrade;     #如果为websocket 则为 upgrade 可晋级的。
    }
server {
    listen 80;
    location /ws {
      proxy_pass http://127.0.0.1:81/;
        proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade; #此处装备 上面界说的变量
            proxy_set_header Connection $connection_upgrade;
    }
    location /rule {
      alias /usr/local/web/;
      index  index  index.html index.htm;
    }
  }

总结

  • 其实之前找过许多类似的组件及前后端联动思路,都不甚理想,没有让人眼前一亮的感觉,直到找到了以上相关的显现组件及后端的结合思路。
  • 这期间消耗了比较多的时刻去选择相关的组件及思路,借用一句用户常常折腾程序猿的话 “我不知道我想要什么,但你做出来了之后我就知道我不想要什么了”,试用结合过,才知道符合不符合。
  • 原本老早之前就要共享,奈何身心俱受创伤,仍是慢疗无效那种,最近强制调节得以喘息,以待后续共享吧。