摘要
本期首要以规矩引擎业务完成为背景,介绍长任务运行日志如何在页面端持续显现更新,用最小的消耗跟踪复杂逻辑的履行情况以便于问题定位。
作用
换不多说先上作用,紧接着跟进完成及处理逻辑
完成思路及组件
长任务当然优先是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;
}
}
总结
- 其实之前找过许多类似的组件及前后端联动思路,都不甚理想,没有让人眼前一亮的感觉,直到找到了以上相关的显现组件及后端的结合思路。
- 这期间消耗了比较多的时刻去选择相关的组件及思路,借用一句用户常常折腾程序猿的话 “我不知道我想要什么,但你做出来了之后我就知道我不想要什么了”,试用结合过,才知道符合不符合。
- 原本老早之前就要共享,奈何身心俱受创伤,仍是慢疗无效那种,最近强制调节得以喘息,以待后续共享吧。