From f5b07ee9064f769d99a440b4922acd7c2135666d Mon Sep 17 00:00:00 2001 From: lijiahang Date: Thu, 25 Apr 2024 16:40:00 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E6=89=A7=E8=A1=8C=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E6=96=87=E4=BB=B6=E8=87=AA=E5=8A=A8=E6=B8=85=E7=90=86?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/about/change-log.md | 5 +- docs/quickstart/faq.md | 14 +-- .../src/main/resources/application.yaml | 8 ++ .../asset/define/config/AppExecLogConfig.java | 40 +++++++ .../asset/task/ExecLogFileAutoClearTask.java | 112 ++++++++++++++++++ .../spring-configuration-metadata.json | 23 ++++ 6 files changed, 194 insertions(+), 8 deletions(-) create mode 100644 orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/define/config/AppExecLogConfig.java create mode 100644 orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/task/ExecLogFileAutoClearTask.java diff --git a/docs/about/change-log.md b/docs/about/change-log.md index d4c4c261..3e117daf 100644 --- a/docs/about/change-log.md +++ b/docs/about/change-log.md @@ -5,16 +5,19 @@ ## v1.0.6 -`2024-05-` `release` +`2024-04-26` `release` * 🐞 修复 终端页签关闭后不会自动切换 * 🩰 修改 命令执行日志 UI 修改 * 🌈 新增 命令执行模板配置默认主机 * 🌈 新增 主机终端书签路径 +* 🌈 新增 命令日志添加状态信息 `app.exec-log.append-status` +* 🌈 新增 定时删除命令日志文件 `app.exec-log.auto-clear` * 🔨 优化 通用分组模型添加 `userId` * 🔨 优化 退出登录不重定向 * 🔨 优化 动态设置页面标题 * 🔨 优化 终端断开后回车重连 +* 🔨 优化 自动删除未使用的命令片段分组 ## v1.0.5 diff --git a/docs/quickstart/faq.md b/docs/quickstart/faq.md index c7a21698..f877a754 100644 --- a/docs/quickstart/faq.md +++ b/docs/quickstart/faq.md @@ -3,24 +3,24 @@ 数据库的数据都采用了逻辑删除, 可以将已删除的数据中的 `deleted` 字段改为 `0` 如果不知道数据是哪一条, 可以查询用户操作日志, 点击 `参数` 寻找操作的id -> ##### 2. 是否支持维护 Windows 主机? - -支持, 但是 Windows 的 ssh 命令兼容性不好, 一切需要执行ssh命令的地方都不友好 - -> ##### 3. 执行命令时为什么会找不到环境变量? +> ##### 2. 执行命令时为什么会找不到环境变量? 可以在执行命令的第一行设置 `source /etc/profile` 来加载环境变量 -> ##### 4. 命令中途执行失败如何设置中断执行? +> ##### 3. 命令中途执行失败如何设置中断执行? 可以在执行命令的第一行设置 `set -e` 作用是: 当执行出现意料之外的情况时, 立即退出 -> ##### 5. 在调度任务、批量执行 命令执行成功的依据是什么? +> ##### 4. 在调度任务、批量执行 命令执行成功的依据是什么? 是获取命令的 `exitcode` 判断是否为 `0` 如果非0则代表命令执行失败 同理, 在命令的最后一行设置 `exit 1` 结果将会是失败, 可以用此来中断后续流程 +> ##### 5. 调度任务、批量执行 的日志文件中如何只保存原始输出? + +修改 application.yaml `app.exec-log.append-status` 为 false + > ##### 6. 为什么使用秘钥认证还是无法连接机器? ``` diff --git a/orion-ops-launch/src/main/resources/application.yaml b/orion-ops-launch/src/main/resources/application.yaml index b4ea3092..d5d53a71 100644 --- a/orion-ops-launch/src/main/resources/application.yaml +++ b/orion-ops-launch/src/main/resources/application.yaml @@ -177,6 +177,14 @@ app: upload-present-backup: true # 备份文件名称 backup-file-name: bk_${fileName}_${timestamp} + # 执行日志 + exec-log: + # 是否拼接执行状态 + append-status: true + # 自动清理执行文件 + auto-clear: true + # 保留周期 + keep-period: 0 # orion framework config orion: diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/define/config/AppExecLogConfig.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/define/config/AppExecLogConfig.java new file mode 100644 index 00000000..3c1493f3 --- /dev/null +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/define/config/AppExecLogConfig.java @@ -0,0 +1,40 @@ +package com.orion.ops.module.asset.define.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 应用执行日志配置 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/4/25 13:36 + */ +@Data +@Component +@ConfigurationProperties(prefix = "app.exec-log") +public class AppExecLogConfig { + + /** + * 是否拼接执行状态 + */ + private Boolean appendStatus; + + /** + * 自动清理执行文件 + */ + private Boolean autoClear; + + /** + * 保留周期 (天) + */ + private Integer keepPeriod; + + public AppExecLogConfig() { + this.appendStatus = true; + this.autoClear = true; + this.keepPeriod = 90; + } + +} diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/task/ExecLogFileAutoClearTask.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/task/ExecLogFileAutoClearTask.java new file mode 100644 index 00000000..1e2f89ae --- /dev/null +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/task/ExecLogFileAutoClearTask.java @@ -0,0 +1,112 @@ +package com.orion.ops.module.asset.task; + +import com.orion.lang.utils.Strings; +import com.orion.lang.utils.io.Files1; +import com.orion.lang.utils.time.Dates; +import com.orion.ops.framework.common.file.FileClient; +import com.orion.ops.module.asset.dao.ExecHostLogDAO; +import com.orion.ops.module.asset.define.config.AppExecLogConfig; +import com.orion.ops.module.asset.entity.domain.ExecHostLogDO; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.io.File; +import java.util.List; + +/** + * 执行日志文件自动清理 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/4/24 23:50 + */ +@Slf4j +@Component +@ConditionalOnProperty(value = "app.exec-log.auto-clear", havingValue = "true") +public class ExecLogFileAutoClearTask { + + /** + * 分布式锁名称 + */ + private static final String LOCK_KEY = "clear:elf:lock"; + + @Resource + private AppExecLogConfig appExecLogConfig; + + @Resource + private RedissonClient redissonClient; + + @Resource + private FileClient logsFileClient; + + @Resource + private ExecHostLogDAO execHostLogDAO; + + /** + * 清理 + */ + // @Scheduled(cron = "0 0 3 * * ?") + @Scheduled(fixedRate = 20000) + public void clear() { + log.info("ExecLogFileAutoClearTask.clear start"); + // 获取锁 + RLock lock = redissonClient.getLock(LOCK_KEY); + // 未获取到直接返回 + if (!lock.tryLock()) { + log.info("ExecLogFileAutoClearTask.clear locked end"); + return; + } + try { + // 清理 + this.doClearFile(); + log.info("ExecLogFileAutoClearTask.clear finish"); + } catch (Exception e) { + log.error("ExecLogFileAutoClearTask.clear error", e); + } finally { + lock.unlock(); + } + } + + /** + * 执行清理文件 + */ + private void doClearFile() { + // 删除的时间区间 + String maxPeriod = Dates.stream() + .subDay(appExecLogConfig.getKeepPeriod()) + .format(); + // 获取需要删除的最大id + ExecHostLogDO hostLog = execHostLogDAO.of() + .createWrapper() + .select(ExecHostLogDO::getLogId, ExecHostLogDO::getLogPath) + .lt(ExecHostLogDO::getCreateTime, maxPeriod) + .orderByDesc(ExecHostLogDO::getId) + .then() + .getOne(); + if (hostLog == null) { + return; + } + // 获取执行日志根目录 + String hostLogPath = logsFileClient.getAbsolutePath(hostLog.getLogPath()); + String execLogPath = Files1.getParentPath(hostLogPath); + String parentPath = Files1.getParentPath(execLogPath); + // 获取需要删除的文件 + List files = Files1.listFilesFilter(parentPath, s -> { + if (!Strings.isInteger(s.getName())) { + return false; + } + return Long.parseLong(s.getName()) <= hostLog.getLogId(); + }, false, true); + if (files.isEmpty()) { + return; + } + // 删除日志文件 + files.forEach(Files1::delete); + } + +} diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/resources/META-INF/spring-configuration-metadata.json b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/resources/META-INF/spring-configuration-metadata.json index f656493c..c8cf99a7 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/resources/META-INF/spring-configuration-metadata.json +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/resources/META-INF/spring-configuration-metadata.json @@ -9,6 +9,11 @@ "name": "app.sftp", "type": "com.orion.ops.module.asset.define.config.AppSftpConfig", "sourceType": "com.orion.ops.module.asset.define.config.AppSftpConfig" + }, + { + "name": "app.exec-log", + "type": "com.orion.ops.module.asset.define.config.AppExecLogConfig", + "sourceType": "com.orion.ops.module.asset.define.config.AppExecLogConfig" } ], "properties": [ @@ -41,6 +46,24 @@ "type": "java.lang.String", "description": "备份文件名称.", "defaultValue": "bk_${fileName}_${timestamp}" + }, + { + "name": "app.exec-log.append-status", + "type": "java.lang.Boolean", + "description": "是否拼接执行状态.", + "defaultValue": "true" + }, + { + "name": "app.exec-log.auto-clear", + "type": "java.lang.Boolean", + "description": "自动清理执行文件.", + "defaultValue": "true" + }, + { + "name": "app.exec-log.keep-period", + "type": "java.lang.Integer", + "description": "保留周期 (天)", + "defaultValue": "90" } ] } \ No newline at end of file