ansi 执行日志.

This commit is contained in:
lijiahang
2024-04-26 11:21:56 +08:00
parent 142c0fff1d
commit af8fedbe44
14 changed files with 358 additions and 68 deletions

View File

@@ -25,13 +25,25 @@ public class ExecCommandDTO {
@Schema(description = "hostId") @Schema(description = "hostId")
private Long logId; private Long logId;
@Schema(description = "用户id")
private Long userId;
@Schema(description = "用户名")
private String username;
@Schema(description = "执行描述")
private String description;
@Schema(description = "执行序列")
private Integer execSeq;
@Schema(description = "超时时间") @Schema(description = "超时时间")
private Integer timeout; private Integer timeout;
@Schema(description = "是否使用脚本执行") @Schema(description = "是否使用脚本执行")
private Boolean scriptExec; private Boolean scriptExec;
@Schema(description = "主机") @Schema(description = "执行主机")
private List<ExecCommandHostDTO> hosts; private List<ExecCommandHostDTO> hosts;
} }

View File

@@ -26,6 +26,12 @@ public class ExecCommandHostDTO {
@Schema(description = "hostId") @Schema(description = "hostId")
private Long hostId; private Long hostId;
@Schema(description = "主机名称")
private String hostName;
@Schema(description = "主机地址")
private String hostAddress;
@Schema(description = "日志文件路径") @Schema(description = "日志文件路径")
private String logPath; private String logPath;
@@ -35,6 +41,9 @@ public class ExecCommandHostDTO {
@Schema(description = "执行命令") @Schema(description = "执行命令")
private String command; private String command;
@Schema(description = "主机用户")
private String username;
@Schema(description = "命令编码") @Schema(description = "命令编码")
private String charset; private String charset;

View File

@@ -10,6 +10,7 @@ import com.orion.lang.support.timeout.TimeoutEndpoint;
import com.orion.lang.utils.Booleans; import com.orion.lang.utils.Booleans;
import com.orion.lang.utils.Exceptions; import com.orion.lang.utils.Exceptions;
import com.orion.lang.utils.Strings; import com.orion.lang.utils.Strings;
import com.orion.lang.utils.ansi.AnsiAppender;
import com.orion.lang.utils.io.Streams; import com.orion.lang.utils.io.Streams;
import com.orion.net.host.SessionStore; import com.orion.net.host.SessionStore;
import com.orion.net.host.sftp.SftpExecutor; import com.orion.net.host.sftp.SftpExecutor;
@@ -32,14 +33,14 @@ import java.util.Date;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
* 命令执行器 * 命令执行器 基类
* *
* @author Jiahang Li * @author Jiahang Li
* @version 1.0.0 * @version 1.0.0
* @since 2024/3/12 11:30 * @since 2024/4/25 18:35
*/ */
@Slf4j @Slf4j
public class ExecCommandHandler implements IExecCommandHandler { public abstract class BaseExecCommandHandler implements IExecCommandHandler {
private final FileClient fileClient = SpringHolder.getBean("logsFileClient"); private final FileClient fileClient = SpringHolder.getBean("logsFileClient");
@@ -49,32 +50,35 @@ public class ExecCommandHandler implements IExecCommandHandler {
private final ExecHostLogDAO execHostLogDAO = SpringHolder.getBean(ExecHostLogDAO.class); private final ExecHostLogDAO execHostLogDAO = SpringHolder.getBean(ExecHostLogDAO.class);
private final ExecCommandDTO execCommand; protected final ExecCommandDTO execCommand;
private final ExecCommandHostDTO execHostCommand; protected final ExecCommandHostDTO execHostCommand;
private final TimeoutChecker<TimeoutEndpoint> timeoutChecker; private final TimeoutChecker<TimeoutEndpoint> timeoutChecker;
@Getter @Getter
private ExecHostStatusEnum status; protected ExecHostStatusEnum status;
protected ExecHostLogDO updateRecord;
private OutputStream logOutputStream;
private SessionStore sessionStore; private SessionStore sessionStore;
private CommandExecutor executor; private CommandExecutor executor;
private OutputStream logOutputStream;
private volatile boolean closed; private volatile boolean closed;
private volatile boolean interrupted; private volatile boolean interrupted;
public ExecCommandHandler(ExecCommandDTO execCommand, public BaseExecCommandHandler(ExecCommandDTO execCommand,
ExecCommandHostDTO execHostCommand, ExecCommandHostDTO execHostCommand,
TimeoutChecker<TimeoutEndpoint> timeoutChecker) { TimeoutChecker<TimeoutEndpoint> timeoutChecker) {
this.status = ExecHostStatusEnum.WAITING; this.status = ExecHostStatusEnum.WAITING;
this.execCommand = execCommand; this.execCommand = execCommand;
this.execHostCommand = execHostCommand; this.execHostCommand = execHostCommand;
this.timeoutChecker = timeoutChecker; this.timeoutChecker = timeoutChecker;
this.updateRecord = new ExecHostLogDO();
} }
@Override @Override
@@ -91,23 +95,25 @@ public class ExecCommandHandler implements IExecCommandHandler {
} catch (Exception e) { } catch (Exception e) {
log.error("ExecCommandHandler run error id: {}", id, e); log.error("ExecCommandHandler run error id: {}", id, e);
ex = e; ex = e;
}
// 执行完成回调
try {
// 回调
this.onFinishCallback(ex);
} finally { } finally {
// 释放资源
Streams.close(this); Streams.close(this);
} }
// 执行回调
if (this.interrupted) {
// 中断执行
this.updateStatus(ExecHostStatusEnum.INTERRUPTED, null);
} else if (ex != null) {
// 执行失败
this.updateStatus(ExecHostStatusEnum.FAILED, ex);
} else if (executor.isTimeout()) {
// 更新执行超时
this.updateStatus(ExecHostStatusEnum.TIMEOUT, null);
} else {
// 更新执行完成
this.updateStatus(ExecHostStatusEnum.COMPLETED, null);
} }
/**
* 初始化日志输出流
*
* @throws Exception Exception
*/
protected void initLogOutputStream() throws Exception {
// 打开日志流
this.logOutputStream = fileClient.getContentOutputStream(execHostCommand.getLogPath());
} }
/** /**
@@ -115,9 +121,9 @@ public class ExecCommandHandler implements IExecCommandHandler {
* *
* @throws IOException IOException * @throws IOException IOException
*/ */
private void execCommand() throws Exception { protected void execCommand() throws Exception {
// 打开日志 // 初始化日志
this.logOutputStream = fileClient.getContentOutputStream(execHostCommand.getLogPath()); this.initLogOutputStream();
// 打开会话 // 打开会话
this.sessionStore = hostTerminalService.openSessionStore(execHostCommand.getHostId()); this.sessionStore = hostTerminalService.openSessionStore(execHostCommand.getHostId());
if (Booleans.isTrue(execCommand.getScriptExec())) { if (Booleans.isTrue(execCommand.getScriptExec())) {
@@ -141,7 +147,7 @@ public class ExecCommandHandler implements IExecCommandHandler {
/** /**
* 上传脚本文件 * 上传脚本文件
*/ */
private void uploadScriptFile() { protected void uploadScriptFile() {
SftpExecutor sftpExecutor = null; SftpExecutor sftpExecutor = null;
try { try {
// 打开 sftp // 打开 sftp
@@ -166,6 +172,42 @@ public class ExecCommandHandler implements IExecCommandHandler {
} }
} }
/**
* 执行完成回调
*
* @param e e
*/
protected void onFinishCallback(Exception e) {
// 执行回调
if (this.interrupted) {
// 中断执行
this.updateStatus(ExecHostStatusEnum.INTERRUPTED, null);
} else if (e != null) {
// 执行失败
this.updateStatus(ExecHostStatusEnum.FAILED, e);
} else if (executor.isTimeout()) {
// 更新执行超时
this.updateStatus(ExecHostStatusEnum.TIMEOUT, null);
} else {
// 更新执行完成
this.updateStatus(ExecHostStatusEnum.COMPLETED, null);
}
}
/**
* 拼接日志
*
* @param appender appender
*/
protected void appendLog(AnsiAppender appender) {
try {
logOutputStream.write(Strings.bytes(appender.toString()));
logOutputStream.flush();
} catch (Exception e) {
log.error("BaseExecCommandHandler.appendLog error", e);
}
}
/** /**
* 更新状态 * 更新状态
* *
@@ -176,30 +218,29 @@ public class ExecCommandHandler implements IExecCommandHandler {
this.status = status; this.status = status;
Long id = execHostCommand.getHostLogId(); Long id = execHostCommand.getHostLogId();
String statusName = status.name(); String statusName = status.name();
log.info("ExecCommandHandler.updateStatus start id: {}, status: {}", id, statusName); log.info("BaseExecCommandHandler.updateStatus start id: {}, status: {}", id, statusName);
ExecHostLogDO update = new ExecHostLogDO(); updateRecord.setId(id);
update.setId(id); updateRecord.setStatus(statusName);
update.setStatus(statusName);
if (ExecHostStatusEnum.RUNNING.equals(status)) { if (ExecHostStatusEnum.RUNNING.equals(status)) {
// 运行中 // 运行中
update.setStartTime(new Date()); updateRecord.setStartTime(new Date());
} else if (ExecHostStatusEnum.COMPLETED.equals(status)) { } else if (ExecHostStatusEnum.COMPLETED.equals(status)) {
// 完成 // 完成
update.setFinishTime(new Date()); updateRecord.setFinishTime(new Date());
update.setExitStatus(executor.getExitCode()); updateRecord.setExitStatus(executor.getExitCode());
} else if (ExecHostStatusEnum.FAILED.equals(status)) { } else if (ExecHostStatusEnum.FAILED.equals(status)) {
// 失败 // 失败
update.setFinishTime(new Date()); updateRecord.setFinishTime(new Date());
update.setErrorMessage(this.getErrorMessage(ex)); updateRecord.setErrorMessage(this.getErrorMessage(ex));
} else if (ExecHostStatusEnum.TIMEOUT.equals(status)) { } else if (ExecHostStatusEnum.TIMEOUT.equals(status)) {
// 超时 // 超时
update.setFinishTime(new Date()); updateRecord.setFinishTime(new Date());
} else if (ExecHostStatusEnum.INTERRUPTED.equals(status)) { } else if (ExecHostStatusEnum.INTERRUPTED.equals(status)) {
// 中断 // 中断
update.setFinishTime(new Date()); updateRecord.setFinishTime(new Date());
} }
int effect = execHostLogDAO.updateById(update); int effect = execHostLogDAO.updateById(updateRecord);
log.info("ExecCommandHandler.updateStatus finish id: {}, effect: {}", id, effect); log.info("BaseExecCommandHandler.updateStatus finish id: {}, effect: {}", id, effect);
} }
@Override @Override
@@ -208,20 +249,21 @@ public class ExecCommandHandler implements IExecCommandHandler {
} }
@Override @Override
public void interrupted() { public void interrupt() {
log.info("ExecCommandHandler.interrupted id: {}, interrupted: {}, closed: {}", log.info("BaseExecCommandHandler.interrupt id: {}, interrupted: {}, closed: {}",
execHostCommand.getHostLogId(), interrupted, closed); execHostCommand.getHostLogId(), interrupted, closed);
if (this.interrupted || this.closed) { if (this.interrupted || this.closed) {
return; return;
} }
// 关闭 // 关闭
this.interrupted = true; this.interrupted = true;
this.close(); Streams.close(executor);
Streams.close(sessionStore);
} }
@Override @Override
public void close() { public void close() {
log.info("ExecCommandHandler.closed id: {}, closed: {}", log.info("BaseExecCommandHandler.closed id: {}, closed: {}",
execHostCommand.getHostLogId(), closed); execHostCommand.getHostLogId(), closed);
if (this.closed) { if (this.closed) {
return; return;
@@ -239,7 +281,7 @@ public class ExecCommandHandler implements IExecCommandHandler {
* @param ex ex * @param ex ex
* @return errorMessage * @return errorMessage
*/ */
private String getErrorMessage(Exception ex) { protected String getErrorMessage(Exception ex) {
String message; String message;
if (ex instanceof InvalidArgumentException) { if (ex instanceof InvalidArgumentException) {
message = ex.getMessage(); message = ex.getMessage();

View File

@@ -0,0 +1,177 @@
package com.orion.ops.module.asset.handler.host.exec.command.handler;
import com.orion.lang.support.timeout.TimeoutChecker;
import com.orion.lang.support.timeout.TimeoutEndpoint;
import com.orion.lang.utils.Booleans;
import com.orion.lang.utils.ansi.AnsiAppender;
import com.orion.lang.utils.ansi.style.AnsiFont;
import com.orion.lang.utils.ansi.style.color.AnsiForeground;
import com.orion.lang.utils.time.Dates;
import com.orion.net.host.ssh.ExitCode;
import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.module.asset.enums.ExecHostStatusEnum;
import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandDTO;
import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandHostDTO;
/**
* 命令执行器 ansi 日志输出
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/4/25 18:19
*/
public class ExecCommandAnsiHandler extends BaseExecCommandHandler {
public ExecCommandAnsiHandler(ExecCommandDTO execCommand, ExecCommandHostDTO execHostCommand, TimeoutChecker<TimeoutEndpoint> timeoutChecker) {
super(execCommand, execHostCommand, timeoutChecker);
}
@Override
protected void initLogOutputStream() throws Exception {
super.initLogOutputStream();
// 拼接启动日志
AnsiAppender appender = AnsiAppender.create()
.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "> 准备执行命令 ")
.append(Dates.current())
.newLine()
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "执行记录: ")
.append(execCommand.getLogId())
.newLine()
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "执行描述: ")
.append(execCommand.getDescription())
.newLine()
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "执行用户: ")
.append(execCommand.getUsername());
// 非系统用户执行添加 userId
if (Const.SYSTEM_USER_ID.equals(execCommand.getUserId())) {
appender.newLine();
} else {
appender.append(" (")
.append(execCommand.getUserId())
.append(")")
.newLine();
}
// 执行序列
if (execCommand.getExecSeq() != null) {
appender.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "执行序列: ")
.append('#')
.append(execCommand.getExecSeq())
.newLine();
}
appender.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "执行主机: ")
.append(execHostCommand.getHostName())
.append(" (")
.append(execHostCommand.getHostId())
.append(")")
.newLine()
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "主机地址: ")
.append(execHostCommand.getHostAddress())
.newLine()
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "超时时间: ")
.append(execCommand.getTimeout())
.newLine()
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "脚本执行: ")
.append(execCommand.getScriptExec())
.newLine()
.newLine()
.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "> 执行命令 ")
.newLine()
.append(execHostCommand.getCommand())
.newLine()
.newLine();
// 非脚本执行拼接开始执行日志
if (!Booleans.isTrue(execCommand.getScriptExec())) {
appender.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "> 开始执行命令 ")
.append(Dates.current())
.newLine();
}
this.appendLog(appender);
}
@Override
protected void uploadScriptFile() {
try {
// 拼接上传日志
AnsiAppender startAppender = AnsiAppender.create()
.newLine()
.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "> 准备上传脚本 ")
.append(Dates.current())
.newLine()
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "文件路径: ")
.append(execHostCommand.getScriptPath())
.newLine();
this.appendLog(startAppender);
// 上传脚本文件
super.uploadScriptFile();
// 拼接完成日志
AnsiAppender finishAppender = AnsiAppender.create()
.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "< 脚本上传成功 ")
.append(Dates.current())
.newLine()
.newLine()
.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "> 开始执行脚本 ")
.append(Dates.current())
.newLine();
this.appendLog(finishAppender);
} catch (Exception e) {
// 拼接失败日志
AnsiAppender errorAppender = AnsiAppender.create()
.append(AnsiForeground.BRIGHT_RED.and(AnsiFont.BOLD), "< 脚本上传失败 ")
.append(Dates.current())
.newLine();
this.appendLog(errorAppender);
throw e;
}
}
@Override
protected void onFinishCallback(Exception e) {
// 执行完成回调
super.onFinishCallback(e);
// 拼接日志
AnsiAppender appender = AnsiAppender.create()
.newLine();
if (this.status == ExecHostStatusEnum.INTERRUPTED) {
// 中断执行
appender.append(AnsiForeground.BRIGHT_YELLOW.and(AnsiFont.BOLD), "< 命令执行中断 ")
.append(Dates.current())
.newLine();
} else if (this.status == ExecHostStatusEnum.FAILED) {
// 执行失败
appender.append(AnsiForeground.BRIGHT_RED.and(AnsiFont.BOLD), "< 命令执行失败 ")
.append(Dates.current())
.newLine()
.append(AnsiForeground.BRIGHT_RED.and(AnsiFont.BOLD), "错误原因: ")
.append(this.getErrorMessage(e))
.newLine();
} else if (this.status == ExecHostStatusEnum.TIMEOUT) {
// 更新执行超时
appender.append(AnsiForeground.BRIGHT_YELLOW.and(AnsiFont.BOLD), "< 命令执行超时 ")
.append(Dates.current())
.newLine();
} else {
long ms = this.updateRecord.getFinishTime().getTime() - this.updateRecord.getStartTime().getTime();
Integer exitStatus = this.updateRecord.getExitStatus();
// 执行完成
appender.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "< 命令执行完成 ")
.append(Dates.current())
.newLine()
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "exit: ");
if (ExitCode.isSuccess(exitStatus)) {
appender.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), exitStatus);
} else {
appender.append(AnsiForeground.BRIGHT_RED.and(AnsiFont.BOLD), exitStatus);
}
appender.newLine()
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "used: ")
.append(Dates.interval(ms, false, "d ", "h ", "m ", "s"))
.append(" (")
.append(ms)
.append(" ms)")
.newLine();
}
// 拼接日志
this.appendLog(appender);
}
}

View File

@@ -0,0 +1,23 @@
package com.orion.ops.module.asset.handler.host.exec.command.handler;
import com.orion.lang.support.timeout.TimeoutChecker;
import com.orion.lang.support.timeout.TimeoutEndpoint;
import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandDTO;
import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandHostDTO;
import lombok.extern.slf4j.Slf4j;
/**
* 命令执行器 原始日志输出
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/3/12 11:30
*/
@Slf4j
public class ExecCommandOriginHandler extends BaseExecCommandHandler {
public ExecCommandOriginHandler(ExecCommandDTO execCommand, ExecCommandHostDTO execHostCommand, TimeoutChecker<TimeoutEndpoint> timeoutChecker) {
super(execCommand, execHostCommand, timeoutChecker);
}
}

View File

@@ -3,11 +3,13 @@ package com.orion.ops.module.asset.handler.host.exec.command.handler;
import com.orion.lang.support.timeout.TimeoutChecker; import com.orion.lang.support.timeout.TimeoutChecker;
import com.orion.lang.support.timeout.TimeoutCheckers; import com.orion.lang.support.timeout.TimeoutCheckers;
import com.orion.lang.support.timeout.TimeoutEndpoint; import com.orion.lang.support.timeout.TimeoutEndpoint;
import com.orion.lang.utils.Booleans;
import com.orion.lang.utils.Threads; import com.orion.lang.utils.Threads;
import com.orion.lang.utils.collect.Lists; import com.orion.lang.utils.collect.Lists;
import com.orion.lang.utils.io.Streams; import com.orion.lang.utils.io.Streams;
import com.orion.ops.module.asset.dao.ExecLogDAO; import com.orion.ops.module.asset.dao.ExecLogDAO;
import com.orion.ops.module.asset.define.AssetThreadPools; import com.orion.ops.module.asset.define.AssetThreadPools;
import com.orion.ops.module.asset.define.config.AppExecLogConfig;
import com.orion.ops.module.asset.entity.domain.ExecLogDO; import com.orion.ops.module.asset.entity.domain.ExecLogDO;
import com.orion.ops.module.asset.enums.ExecStatusEnum; import com.orion.ops.module.asset.enums.ExecStatusEnum;
import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandDTO; import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandDTO;
@@ -34,6 +36,8 @@ public class ExecTaskHandler implements IExecTaskHandler {
private static final ExecTaskManager execTaskManager = SpringHolder.getBean(ExecTaskManager.class); private static final ExecTaskManager execTaskManager = SpringHolder.getBean(ExecTaskManager.class);
private static final AppExecLogConfig appExecLogConfig = SpringHolder.getBean(AppExecLogConfig.class);
private final ExecCommandDTO execCommand; private final ExecCommandDTO execCommand;
private TimeoutChecker<TimeoutEndpoint> timeoutChecker; private TimeoutChecker<TimeoutEndpoint> timeoutChecker;
@@ -73,9 +77,9 @@ public class ExecTaskHandler implements IExecTaskHandler {
} }
@Override @Override
public void interrupted() { public void interrupt() {
log.info("ExecTaskHandler-interrupted id: {}", execCommand.getLogId()); log.info("ExecTaskHandler-interrupt id: {}", execCommand.getLogId());
handlers.forEach(IExecCommandHandler::interrupted); handlers.forEach(IExecCommandHandler::interrupt);
} }
/** /**
@@ -93,18 +97,34 @@ public class ExecTaskHandler implements IExecTaskHandler {
List<ExecCommandHostDTO> hosts = execCommand.getHosts(); List<ExecCommandHostDTO> hosts = execCommand.getHosts();
if (hosts.size() == 1) { if (hosts.size() == 1) {
// 单个主机直接执行 // 单个主机直接执行
ExecCommandHandler handler = new ExecCommandHandler(execCommand, hosts.get(0), timeoutChecker); IExecCommandHandler handler = this.createCommandHandler(hosts.get(0));
handlers.add(handler); handlers.add(handler);
handler.run(); handler.run();
} else { } else {
hosts.stream() hosts.stream()
.map(s -> new ExecCommandHandler(execCommand, s, timeoutChecker)) .map(this::createCommandHandler)
.forEach(handlers::add); .forEach(handlers::add);
// 多个主机异步阻塞执行 // 多个主机异步阻塞执行
Threads.blockRun(handlers, AssetThreadPools.EXEC_HOST); Threads.blockRun(handlers, AssetThreadPools.EXEC_HOST);
} }
} }
/**
* 创建命令执行器
*
* @param host host
* @return handler
*/
private IExecCommandHandler createCommandHandler(ExecCommandHostDTO host) {
if (Booleans.isTrue(appExecLogConfig.getAppendAnsi())) {
// ansi 日志
return new ExecCommandAnsiHandler(execCommand, host, timeoutChecker);
} else {
// 原始日志
return new ExecCommandOriginHandler(execCommand, host, timeoutChecker);
}
}
/** /**
* 更新状态 * 更新状态
* *

View File

@@ -22,7 +22,7 @@ public interface IExecCommandHandler extends Runnable, SafeCloseable {
/** /**
* 中断执行 * 中断执行
*/ */
void interrupted(); void interrupt();
/** /**
* 获取当前状态 * 获取当前状态

View File

@@ -23,6 +23,6 @@ public interface IExecTaskHandler extends Runnable, SafeCloseable {
/** /**
* 中断执行 * 中断执行
*/ */
void interrupted(); void interrupt();
} }

View File

@@ -129,7 +129,7 @@ public class SftpSession extends TerminalSession implements ISftpSession {
throw Exceptions.ioRuntime(e); throw Exceptions.ioRuntime(e);
} finally { } finally {
// 同关闭 transfer downloader // 同关闭 transfer downloader
// 关闭 inputStream 可能会被阻塞 ??..?? 只能关闭 executor // 关闭 inputStream 可能会被阻塞 ???...??? 只能关闭 executor
Streams.close(this.executor); Streams.close(this.executor);
this.connect(); this.connect();
} }

View File

@@ -98,7 +98,7 @@ public class DownloadSession extends TransferHostSession implements IDownloadSes
@Override @Override
protected void closeStream() { protected void closeStream() {
// 关闭 inputStream 可能会被阻塞 ??..?? 只能关闭 executor // 关闭 inputStream 可能会被阻塞 ???...??? 只能关闭 executor
Streams.close(this.executor); Streams.close(this.executor);
this.executor = null; this.executor = null;
this.inputStream = null; this.inputStream = null;

View File

@@ -197,9 +197,12 @@ public class ExecCommandServiceImpl implements ExecCommandService {
return ExecCommandHostDTO.builder() return ExecCommandHostDTO.builder()
.hostId(s.getHostId()) .hostId(s.getHostId())
.hostLogId(s.getId()) .hostLogId(s.getId())
.hostName(s.getHostName())
.hostAddress(s.getHostAddress())
.command(s.getCommand()) .command(s.getCommand())
.logPath(s.getLogPath()) .logPath(s.getLogPath())
.scriptPath(s.getScriptPath()) .scriptPath(s.getScriptPath())
.username(config.getUsername())
.charset(config.getCharset()) .charset(config.getCharset())
.fileNameCharset(config.getFileNameCharset()) .fileNameCharset(config.getFileNameCharset())
.fileContentCharset(config.getFileContentCharset()) .fileContentCharset(config.getFileContentCharset())
@@ -208,6 +211,10 @@ public class ExecCommandServiceImpl implements ExecCommandService {
// 执行信息 // 执行信息
ExecCommandDTO exec = ExecCommandDTO.builder() ExecCommandDTO exec = ExecCommandDTO.builder()
.logId(execLog.getId()) .logId(execLog.getId())
.userId(execLog.getUserId())
.username(execLog.getUsername())
.description(execLog.getDescription())
.execSeq(execLog.getExecSeq())
.timeout(execLog.getTimeout()) .timeout(execLog.getTimeout())
.scriptExec(ScriptExecEnum.isEnabled(execLog.getScriptExec())) .scriptExec(ScriptExecEnum.isEnabled(execLog.getScriptExec()))
.hosts(hosts) .hosts(hosts)

View File

@@ -79,7 +79,7 @@ public class ExecHostLogServiceImpl implements ExecHostLogService {
.flatMap(s -> s.stream() .flatMap(s -> s.stream()
.filter(h -> h.getHostId().equals(record.getHostId())) .filter(h -> h.getHostId().equals(record.getHostId()))
.findFirst()) .findFirst())
.ifPresent(IExecCommandHandler::interrupted); .ifPresent(IExecCommandHandler::interrupt);
// 删除 // 删除
int effect = execHostLogDAO.deleteById(id); int effect = execHostLogDAO.deleteById(id);
log.info("ExecHostLogService-deleteExecHostLogById id: {}, effect: {}", id, effect); log.info("ExecHostLogService-deleteExecHostLogById id: {}, effect: {}", id, effect);

View File

@@ -176,7 +176,7 @@ public class ExecLogServiceImpl implements ExecLogService {
ExecLogDO record = execLogDAO.selectByIdSource(id, source); ExecLogDO record = execLogDAO.selectByIdSource(id, source);
Valid.notNull(record, ErrorMessage.DATA_ABSENT); Valid.notNull(record, ErrorMessage.DATA_ABSENT);
// 中断命令执行 // 中断命令执行
this.interruptedTask(Lists.singleton(id)); this.interruptTask(Lists.singleton(id));
// 删除执行日志 // 删除执行日志
int effect = execLogDAO.deleteById(id); int effect = execLogDAO.deleteById(id);
// 删除主机日志 // 删除主机日志
@@ -200,7 +200,7 @@ public class ExecLogServiceImpl implements ExecLogService {
.intValue(); .intValue();
Valid.isTrue(idList.size() == count, ErrorMessage.DATA_MODIFIED); Valid.isTrue(idList.size() == count, ErrorMessage.DATA_MODIFIED);
// 中断命令执行 // 中断命令执行
this.interruptedTask(idList); this.interruptTask(idList);
// 删除执行日志 // 删除执行日志
int effect = execLogDAO.deleteBatchIds(idList); int effect = execLogDAO.deleteBatchIds(idList);
// 删除主机日志 // 删除主机日志
@@ -230,7 +230,7 @@ public class ExecLogServiceImpl implements ExecLogService {
int effect = 0; int effect = 0;
if (!idList.isEmpty()) { if (!idList.isEmpty()) {
// 中断命令执行 // 中断命令执行
this.interruptedTask(idList); this.interruptTask(idList);
// 删除执行日志 // 删除执行日志
effect = execLogDAO.delete(wrapper); effect = execLogDAO.delete(wrapper);
// 删除主机日志 // 删除主机日志
@@ -256,9 +256,9 @@ public class ExecLogServiceImpl implements ExecLogService {
// 中断执行 // 中断执行
IExecTaskHandler task = execTaskManager.getTask(logId); IExecTaskHandler task = execTaskManager.getTask(logId);
if (task != null) { if (task != null) {
log.info("ExecLogService.interruptExec interrupted logId: {}", logId); log.info("ExecLogService.interruptExec interrupt logId: {}", logId);
// 中断 // 中断
task.interrupted(); task.interrupt();
} else { } else {
log.info("ExecLogService.interruptExec updateStatus start logId: {}", logId); log.info("ExecLogService.interruptExec updateStatus start logId: {}", logId);
// 不存在则直接修改状态 // 不存在则直接修改状态
@@ -299,7 +299,7 @@ public class ExecLogServiceImpl implements ExecLogService {
// 中断执行 // 中断执行
IExecTaskHandler task = execTaskManager.getTask(logId); IExecTaskHandler task = execTaskManager.getTask(logId);
if (task != null) { if (task != null) {
log.info("ExecLogService.interruptHostExec interrupted logId: {}, hostLogId: {}", logId, hostLogId); log.info("ExecLogService.interruptHostExec interrupt logId: {}, hostLogId: {}", logId, hostLogId);
IExecCommandHandler handler = task.getHandlers() IExecCommandHandler handler = task.getHandlers()
.stream() .stream()
.filter(s -> s.getHostId().equals(hostLog.getHostId())) .filter(s -> s.getHostId().equals(hostLog.getHostId()))
@@ -307,7 +307,7 @@ public class ExecLogServiceImpl implements ExecLogService {
.orElse(null); .orElse(null);
// 中断 // 中断
if (handler != null) { if (handler != null) {
handler.interrupted(); handler.interrupt();
} }
} else { } else {
log.info("ExecLogService.interruptHostExec updateStatus start logId: {}, hostLogId: {}", logId, hostLogId); log.info("ExecLogService.interruptHostExec updateStatus start logId: {}, hostLogId: {}", logId, hostLogId);
@@ -465,11 +465,11 @@ public class ExecLogServiceImpl implements ExecLogService {
* *
* @param idList idList * @param idList idList
*/ */
private void interruptedTask(List<Long> idList) { private void interruptTask(List<Long> idList) {
idList.stream() idList.stream()
.map(execTaskManager::getTask) .map(execTaskManager::getTask)
.filter(Objects::nonNull) .filter(Objects::nonNull)
.forEach(IExecTaskHandler::interrupted); .forEach(IExecTaskHandler::interrupt);
} }
} }

View File

@@ -26,7 +26,7 @@ import java.util.List;
*/ */
@Slf4j @Slf4j
@Component @Component
@ConditionalOnProperty(value = "app.exec-log.auto-clear", havingValue = "true") @ConditionalOnProperty(value = "app.exec-log.auto-clear", havingValue = "true", matchIfMissing = true)
public class ExecLogFileAutoClearTask { public class ExecLogFileAutoClearTask {
/** /**