From abd246828e65a9dca8cd768fbde52647e6a33d50 Mon Sep 17 00:00:00 2001 From: bwcx_jzy Date: Fri, 24 Dec 2021 23:22:22 +0800 Subject: [PATCH] =?UTF-8?q?=E8=84=9A=E6=9C=AC=E6=97=A5=E5=BF=97=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/script/ScriptController.java | 68 ++++++++ .../io/jpom/socket/ScriptProcessBuilder.java | 29 ++++ .../java/io/jpom/plugin/ClassFeature.java | 1 + .../java/io/jpom/common/forward/NodeUrl.java | 2 + .../node/script/ScriptLogController.java | 83 ++++++++++ .../model/data/ScriptExecuteLogModel.java | 13 ++ .../node/script/ScriptExecuteLogServer.java | 52 +++++- .../io/jpom/socket/handler/ScriptHandler.java | 3 +- .../src/main/resources/menus/node-index.json | 4 + .../src/main/resources/sql/h2-db-v3.1.sql | 3 + web-vue/src/api/node-other.js | 32 +++- web-vue/src/pages/node/node-layout/index.vue | 150 ++++++++++-------- .../node/node-layout/other/script-list.vue | 15 +- .../node-layout/other/script-log-view.vue | 88 ++++++++++ .../node/node-layout/other/script-log.vue | 142 +++++++++++++++++ web-vue/src/pages/system/log.vue | 2 +- 16 files changed, 604 insertions(+), 83 deletions(-) create mode 100644 modules/server/src/main/java/io/jpom/controller/node/script/ScriptLogController.java create mode 100644 web-vue/src/pages/node/node-layout/other/script-log-view.vue create mode 100644 web-vue/src/pages/node/node-layout/other/script-log.vue diff --git a/modules/agent/src/main/java/io/jpom/controller/script/ScriptController.java b/modules/agent/src/main/java/io/jpom/controller/script/ScriptController.java index 73b81f28d..99382b808 100644 --- a/modules/agent/src/main/java/io/jpom/controller/script/ScriptController.java +++ b/modules/agent/src/main/java/io/jpom/controller/script/ScriptController.java @@ -22,17 +22,26 @@ */ package io.jpom.controller.script; +import cn.hutool.core.convert.Convert; import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.LineHandler; +import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.crypto.SecureUtil; import cn.hutool.http.HtmlUtil; import cn.jiangzeyin.common.JsonMessage; +import cn.jiangzeyin.common.validator.ValidatorItem; +import cn.jiangzeyin.common.validator.ValidatorRule; import cn.jiangzeyin.controller.multipart.MultipartFileBuilder; +import com.alibaba.fastjson.JSONObject; import io.jpom.common.BaseAgentController; import io.jpom.model.data.NodeScriptModel; import io.jpom.service.script.ScriptServer; +import io.jpom.socket.ScriptProcessBuilder; import io.jpom.system.AgentConfigBean; import io.jpom.system.ExtConfigBean; +import io.jpom.util.CommandUtil; +import io.jpom.util.LimitQueue; import org.springframework.http.MediaType; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.RequestMapping; @@ -128,4 +137,63 @@ public class ScriptController extends BaseAgentController { scriptServer.addItem(eModel); return JsonMessage.getString(200, "导入成功"); } + + /** + * 获取的日志 + * + * @param id id + * @param executeId 执行ID + * @param line 需要获取的行号 + * @return json + */ + @RequestMapping(value = "log", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) + public String getNowLog(@ValidatorItem() String id, + @ValidatorItem() String executeId, + @ValidatorItem(value = ValidatorRule.POSITIVE_INTEGER, msg = "line") int line) { + NodeScriptModel item = scriptServer.getItem(id); + Assert.notNull(item, "没有对应数据"); + File logFile = item.logFile(executeId); + Assert.state(FileUtil.isFile(logFile), "日志文件错误"); + + JSONObject data = new JSONObject(); + // 运行中 + data.put("run", ScriptProcessBuilder.isRun(executeId)); + // 读取文件 + int linesInt = Convert.toInt(line, 1); + LimitQueue lines = new LimitQueue<>(1000); + final int[] readCount = {0}; + FileUtil.readLines(logFile, CharsetUtil.CHARSET_UTF_8, (LineHandler) line1 -> { + readCount[0]++; + if (readCount[0] < linesInt) { + return; + } + lines.add(line1); + }); + // 下次应该获取的行数 + data.put("line", readCount[0] + 1); + data.put("getLine", linesInt); + data.put("dataLines", lines); + return JsonMessage.getString(200, "ok", data); + } + + /** + * 删除日志 + * + * @param id id + * @param executeId 执行ID + * @return json + */ + @RequestMapping(value = "del_log", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) + public String delLog(@ValidatorItem() String id, + @ValidatorItem() String executeId) { + NodeScriptModel item = scriptServer.getItem(id); + if (item == null) { + return JsonMessage.getString(200, "对应的脚本模版已经不存在拉"); + } + Assert.notNull(item, "没有对应数据"); + File logFile = item.logFile(executeId); + boolean fastDel = CommandUtil.systemFastDel(logFile); + Assert.state(!fastDel, "删除日志文件失败"); + return JsonMessage.getString(200, "删除成功"); + } } diff --git a/modules/agent/src/main/java/io/jpom/socket/ScriptProcessBuilder.java b/modules/agent/src/main/java/io/jpom/socket/ScriptProcessBuilder.java index 0d1e0fcef..fd08142e6 100644 --- a/modules/agent/src/main/java/io/jpom/socket/ScriptProcessBuilder.java +++ b/modules/agent/src/main/java/io/jpom/socket/ScriptProcessBuilder.java @@ -23,6 +23,7 @@ package io.jpom.socket; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.LineHandler; @@ -52,6 +53,9 @@ import java.util.concurrent.ConcurrentHashMap; * @date 2019/4/25 */ public class ScriptProcessBuilder implements Runnable { + /** + * 执行中的缓存 + */ private static final ConcurrentHashMap FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP = new ConcurrentHashMap<>(); private final ProcessBuilder processBuilder; @@ -79,6 +83,14 @@ public class ScriptProcessBuilder implements Runnable { processBuilder.command(command); } + /** + * 创建执行 并监听 + * + * @param nodeScriptModel 脚本模版 + * @param executeId 执行ID + * @param args 参数 + * @param session 会话 + */ public static void addWatcher(NodeScriptModel nodeScriptModel, String executeId, String args, Session session) { ScriptProcessBuilder scriptProcessBuilder = FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.computeIfAbsent(executeId, file1 -> { ScriptProcessBuilder scriptProcessBuilder1 = new ScriptProcessBuilder(nodeScriptModel, executeId, args); @@ -99,6 +111,21 @@ public class ScriptProcessBuilder implements Runnable { } } + /** + * 判断是否还在执行中 + * + * @param executeId 执行id + * @return true 还在执行 + */ + public static boolean isRun(String executeId) { + return FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.containsKey(executeId); + } + + /** + * 关闭会话 + * + * @param session 会话 + */ public static void stopWatcher(Session session) { Collection scriptProcessBuilders = FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.values(); for (ScriptProcessBuilder scriptProcessBuilder : scriptProcessBuilders) { @@ -123,6 +150,7 @@ public class ScriptProcessBuilder implements Runnable { public void run() { //初始化ProcessBuilder对象 try { + this.handle("start execute:" + DateUtil.now()); process = processBuilder.start(); { inputStream = process.getInputStream(); @@ -133,6 +161,7 @@ public class ScriptProcessBuilder implements Runnable { JSONObject jsonObject = jsonMessage.toJson(); jsonObject.put("op", ConsoleCommandOp.stop.name()); this.end(jsonObject.toString()); + this.handle("execute done:" + waitFor + " time:" + DateUtil.now()); } catch (Exception e) { DefaultSystemLog.getLog().error("执行异常", e); this.end("执行异常:" + e.getMessage()); diff --git a/modules/common/src/main/java/io/jpom/plugin/ClassFeature.java b/modules/common/src/main/java/io/jpom/plugin/ClassFeature.java index 86145c5fe..40f425788 100644 --- a/modules/common/src/main/java/io/jpom/plugin/ClassFeature.java +++ b/modules/common/src/main/java/io/jpom/plugin/ClassFeature.java @@ -72,6 +72,7 @@ public enum ClassFeature { PROJECT_CONSOLE("项目控制台", ClassFeature.NODE), JDK_LIST("JDK管理", ClassFeature.NODE), NODE_SCRIPT("脚本模板", ClassFeature.NODE), + NODE_SCRIPT_LOG("脚本模板日志", ClassFeature.NODE), TOMCAT("Tomcat", ClassFeature.NODE), TOMCAT_FILE("Tomcat file", ClassFeature.NODE), TOMCAT_LOG("Tomcat log", ClassFeature.NODE), diff --git a/modules/server/src/main/java/io/jpom/common/forward/NodeUrl.java b/modules/server/src/main/java/io/jpom/common/forward/NodeUrl.java index 6a3501605..f2e923a1b 100644 --- a/modules/server/src/main/java/io/jpom/common/forward/NodeUrl.java +++ b/modules/server/src/main/java/io/jpom/common/forward/NodeUrl.java @@ -172,6 +172,8 @@ public enum NodeUrl { Script_List("/script/list.json"), Script_Item("/script/item.json"), Script_Save("/script/save.json"), + SCRIPT_LOG("/script/log"), + SCRIPT_DEL_LOG("/script/del_log"), Script_Upload("/script/upload.json"), Script_Del("/script/del.json"), diff --git a/modules/server/src/main/java/io/jpom/controller/node/script/ScriptLogController.java b/modules/server/src/main/java/io/jpom/controller/node/script/ScriptLogController.java new file mode 100644 index 000000000..7a8b20659 --- /dev/null +++ b/modules/server/src/main/java/io/jpom/controller/node/script/ScriptLogController.java @@ -0,0 +1,83 @@ +package io.jpom.controller.node.script; + +import cn.jiangzeyin.common.JsonMessage; +import cn.jiangzeyin.common.validator.ValidatorItem; +import io.jpom.common.BaseServerController; +import io.jpom.common.forward.NodeForward; +import io.jpom.common.forward.NodeUrl; +import io.jpom.model.PageResultDto; +import io.jpom.model.data.NodeModel; +import io.jpom.model.data.ScriptExecuteLogModel; +import io.jpom.plugin.ClassFeature; +import io.jpom.plugin.Feature; +import io.jpom.plugin.MethodFeature; +import io.jpom.service.node.script.ScriptExecuteLogServer; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.util.Assert; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author bwcx_jzy + * @since 2021/12/24 + */ +@RestController +@RequestMapping(value = "/node/script_log") +@Feature(cls = ClassFeature.NODE_SCRIPT_LOG) +public class ScriptLogController extends BaseServerController { + + private final ScriptExecuteLogServer scriptExecuteLogServer; + + public ScriptLogController(ScriptExecuteLogServer scriptExecuteLogServer) { + this.scriptExecuteLogServer = scriptExecuteLogServer; + } + + /** + * get script log list + * + * @return json + */ + @RequestMapping(value = "list", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) + public String scriptList() { + PageResultDto pageResultDto = scriptExecuteLogServer.listPageNode(getRequest()); + return JsonMessage.getString(200, "", pageResultDto); + } + + /** + * 查日志 + * + * @return json + */ + @RequestMapping(value = "log", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) + @Feature(method = MethodFeature.LIST) + public String log() { + NodeModel node = getNode(); + return NodeForward.request(node, getRequest(), NodeUrl.SCRIPT_LOG).toString(); + } + + /** + * 删除日志 + * + * @param id 模版ID + * @param executeId 日志ID + * @return json + */ + @RequestMapping(value = "del", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) + @Feature(method = MethodFeature.DEL) + public String del(@ValidatorItem String id, String executeId) { + NodeModel node = getNode(); + ScriptExecuteLogModel scriptExecuteLogModel = new ScriptExecuteLogModel(); + scriptExecuteLogModel.setId(executeId); + scriptExecuteLogModel.setScriptId(id); + scriptExecuteLogModel.setNodeId(node.getId()); + ScriptExecuteLogModel executeLogModel = scriptExecuteLogServer.queryByBean(scriptExecuteLogModel); + Assert.notNull(executeLogModel, "没有对应的执行日志"); + JsonMessage request = NodeForward.request(node, getRequest(), NodeUrl.SCRIPT_DEL_LOG); + if (request.getCode() == HttpStatus.OK.value()) { + scriptExecuteLogServer.delByKey(executeId); + } + return request.toString(); + } +} diff --git a/modules/server/src/main/java/io/jpom/model/data/ScriptExecuteLogModel.java b/modules/server/src/main/java/io/jpom/model/data/ScriptExecuteLogModel.java index 210a5d07e..4359ad19b 100644 --- a/modules/server/src/main/java/io/jpom/model/data/ScriptExecuteLogModel.java +++ b/modules/server/src/main/java/io/jpom/model/data/ScriptExecuteLogModel.java @@ -37,6 +37,19 @@ public class ScriptExecuteLogModel extends BaseNodeModel { */ private String scriptId; + /** + * 脚本名称 + */ + private String scriptName; + + public String getScriptName() { + return scriptName; + } + + public void setScriptName(String scriptName) { + this.scriptName = scriptName; + } + public String getScriptId() { return scriptId; } diff --git a/modules/server/src/main/java/io/jpom/service/node/script/ScriptExecuteLogServer.java b/modules/server/src/main/java/io/jpom/service/node/script/ScriptExecuteLogServer.java index 4b6feeaf7..5a0769556 100644 --- a/modules/server/src/main/java/io/jpom/service/node/script/ScriptExecuteLogServer.java +++ b/modules/server/src/main/java/io/jpom/service/node/script/ScriptExecuteLogServer.java @@ -1,7 +1,17 @@ package io.jpom.service.node.script; +import cn.hutool.http.HttpStatus; +import cn.jiangzeyin.common.DefaultSystemLog; +import cn.jiangzeyin.common.JsonMessage; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import io.jpom.common.forward.NodeForward; +import io.jpom.common.forward.NodeUrl; +import io.jpom.model.data.NodeModel; import io.jpom.model.data.ScriptExecuteLogModel; -import io.jpom.service.h2db.BaseWorkspaceService; +import io.jpom.service.h2db.BaseNodeService; +import io.jpom.service.node.NodeService; +import io.jpom.service.system.WorkspaceService; import org.springframework.stereotype.Service; /** @@ -11,10 +21,48 @@ import org.springframework.stereotype.Service; * @since 2021/12/12 */ @Service -public class ScriptExecuteLogServer extends BaseWorkspaceService { +public class ScriptExecuteLogServer extends BaseNodeService { + + protected ScriptExecuteLogServer(NodeService nodeService, + WorkspaceService workspaceService) { + super(nodeService, workspaceService, "脚本模版日志"); + } @Override protected String[] clearTimeColumns() { return new String[]{"createTimeMillis"}; } + + @Override + public JSONObject getItem(NodeModel nodeModel, String id) { + return null; + } + + @Override + public JSONArray getLitDataArray(NodeModel nodeModel) { + return null; + } + + @Override + public void syncAllNode() { + // + } + + @Override + protected void executeClearImpl(int h2DbLogStorageCount) { + super.autoLoopClear("createTimeMillis", h2DbLogStorageCount, + null, + executeLogModel -> { + try { + NodeModel nodeModel = nodeService.getByKey(executeLogModel.getNodeId()); + JsonMessage jsonMessage = NodeForward.requestBySys(nodeModel, NodeUrl.SCRIPT_DEL_LOG, + "id", executeLogModel.getScriptId(), "executeId", executeLogModel.getId()); + if (jsonMessage.getCode() != HttpStatus.HTTP_OK) { + DefaultSystemLog.getLog().info(jsonMessage.toString()); + } + } catch (Exception e) { + DefaultSystemLog.getLog().error("自动清除数据错误", e); + } + }); + } } diff --git a/modules/server/src/main/java/io/jpom/socket/handler/ScriptHandler.java b/modules/server/src/main/java/io/jpom/socket/handler/ScriptHandler.java index 0c4790f6d..f10e66108 100644 --- a/modules/server/src/main/java/io/jpom/socket/handler/ScriptHandler.java +++ b/modules/server/src/main/java/io/jpom/socket/handler/ScriptHandler.java @@ -85,16 +85,17 @@ public class ScriptHandler extends BaseProxyHandler { ScriptServer scriptServer = SpringUtil.getBean(ScriptServer.class); // try { + BaseServerController.resetInfo(userModel); // ScriptModel scriptModel = new ScriptModel(); scriptModel.setId(dataItem.getId()); scriptModel.setLastRunUser(userModel.getId()); scriptServer.update(scriptModel); // - BaseServerController.resetInfo(userModel); ScriptExecuteLogModel scriptExecuteLogModel = new ScriptExecuteLogModel(); scriptExecuteLogModel.setScriptId(dataItem.getScriptId()); scriptExecuteLogModel.setNodeId(nodeInfo.getId()); + scriptExecuteLogModel.setScriptName(dataItem.getName()); scriptExecuteLogModel.setWorkspaceId(nodeInfo.getWorkspaceId()); logServer.insert(scriptExecuteLogModel); return scriptExecuteLogModel.getId(); diff --git a/modules/server/src/main/resources/menus/node-index.json b/modules/server/src/main/resources/menus/node-index.json index 40639f144..77d5cd32f 100644 --- a/modules/server/src/main/resources/menus/node-index.json +++ b/modules/server/src/main/resources/menus/node-index.json @@ -33,6 +33,10 @@ { "id": "script", "title": "脚本模板" + }, + { + "id": "script-log", + "title": "脚本执行记录" } ] }, diff --git a/modules/server/src/main/resources/sql/h2-db-v3.1.sql b/modules/server/src/main/resources/sql/h2-db-v3.1.sql index 113fb39bf..520c2ab47 100644 --- a/modules/server/src/main/resources/sql/h2-db-v3.1.sql +++ b/modules/server/src/main/resources/sql/h2-db-v3.1.sql @@ -18,3 +18,6 @@ ALTER TABLE COMMAND_EXEC_LOG -- 分发状态 ALTER TABLE OUT_GIVING ADD IF NOT EXISTS `status` int default '0' comment '状态{0: 未分发; 1: 分发中; 2: 分发结束}'; + +ALTER TABLE SCRIPT_EXECUTE_LOG + ADD IF NOT EXISTS scriptName VARCHAR (100) comment '脚本名称'; diff --git a/web-vue/src/api/node-other.js b/web-vue/src/api/node-other.js index dc9329d93..7c35adf56 100644 --- a/web-vue/src/api/node-other.js +++ b/web-vue/src/api/node-other.js @@ -256,7 +256,7 @@ export function getScriptList(params) { } /** - * script 列表 + * script 服务端中的所有列表 */ export function getScriptListAll(params) { return axios({ @@ -266,6 +266,36 @@ export function getScriptListAll(params) { }); } +// 脚本模版日志列表 +export function getScriptLogList(params) { + return axios({ + url: "/node/script_log/list", + method: "post", + data: params, + }); +} + +// 删除执行记录 +export function scriptDel(params) { + return axios({ + url: "/node/script_log/del", + method: "post", + data: params, + }); +} + +//执行记录 详情 +export function scriptLog(params) { + return axios({ + url: "/node/script_log/log", + method: "post", + data: params, + headers: { + tip: "no", + }, + }); +} + /** * Script 编辑 * @param {nodeId, id, name, path, port, appBase} params diff --git a/web-vue/src/pages/node/node-layout/index.vue b/web-vue/src/pages/node/node-layout/index.vue index f7482c8f3..c132e49d9 100644 --- a/web-vue/src/pages/node/node-layout/index.vue +++ b/web-vue/src/pages/node/node-layout/index.vue @@ -7,15 +7,15 @@ - {{menu.title}} + {{ menu.title }} - {{subMenu.title}} + {{ subMenu.title }} - {{menu.title}} + {{ menu.title }} @@ -28,6 +28,7 @@ + @@ -39,22 +40,23 @@ diff --git a/web-vue/src/pages/node/node-layout/other/script-log-view.vue b/web-vue/src/pages/node/node-layout/other/script-log-view.vue new file mode 100644 index 000000000..eeb497dd8 --- /dev/null +++ b/web-vue/src/pages/node/node-layout/other/script-log-view.vue @@ -0,0 +1,88 @@ + + + diff --git a/web-vue/src/pages/node/node-layout/other/script-log.vue b/web-vue/src/pages/node/node-layout/other/script-log.vue new file mode 100644 index 000000000..d5ce2e67a --- /dev/null +++ b/web-vue/src/pages/node/node-layout/other/script-log.vue @@ -0,0 +1,142 @@ + + + diff --git a/web-vue/src/pages/system/log.vue b/web-vue/src/pages/system/log.vue index a52e4ea21..5b61d298a 100644 --- a/web-vue/src/pages/system/log.vue +++ b/web-vue/src/pages/system/log.vue @@ -147,7 +147,7 @@ export default { this.socket.onerror = (err) => { console.error(err); this.$notification.error({ - message: "socket 错误,请检查是否开启 ws 代理,或者没有对应的权限", + message: "web socket 错误,请检查是否开启 ws 代理,或者没有对应的权限", }); }; },