执行日志文件自动清理.

This commit is contained in:
lijiahang
2024-04-25 16:40:00 +08:00
parent d52c1f4d88
commit f5b07ee906
6 changed files with 194 additions and 8 deletions

View File

@@ -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

View File

@@ -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. 为什么使用秘钥认证还是无法连接机器?
```

View File

@@ -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:

View File

@@ -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;
}
}

View File

@@ -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<File> 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);
}
}

View File

@@ -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"
}
]
}