SpringBoot 集成Shell命令实现系统日志查询并通过API返回给前端

2023-05-16

1.目的

在做物联网项目中,为了方便产品测试人员以及第三方产品集成商能通过系统快速获取设备原始数据,因此考虑在物联网终端管理平台增加读取物联网通讯服务记录的终端日志的功能。

我的想法是直接通过Java操作Linux服务器的Shell命令,读取日志文件里面的上下行数据。

2.集成Shell命令

2.1.引入Jar包

        <dependency>
            <groupId>org.apache.sshd</groupId>
            <artifactId>sshd-core</artifactId>
            <version>2.8.0</version>
        </dependency>

2.2.封装Shell方法操作类

2.2.1.核心方法

/**
 * SSH linux操作类
 * @author lenny
 * @date 20230130
 */
public class SSHLinuxUtils {

    protected SSHLinuxUtils() {
    }

    /**
     * 执行Shell命令并返回结果
     * @param conn
     * @param cmd
     * @param timeout
     * @return
     * @throws IOException
     */
    public static SshResponse runCommand(SshConnection conn, String cmd, long timeout)
            throws IOException {
        SshClient client = SshClient.setUpDefaultClient();
        try {
            //Open the client
            client.start();
            //Connect to the server
            ConnectFuture cf = client.connect(conn.getUsername(), conn.getHostname(), 22);
            ClientSession session = cf.verify().getSession();
            session.addPasswordIdentity(conn.getPassword());
            session.auth().verify(TimeUnit.SECONDS.toMillis(timeout));
            //Create the exec and channel its output/error streams
            ChannelExec ce = session.createExecChannel(cmd);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ByteArrayOutputStream err = new ByteArrayOutputStream();
            ce.setOut(out);
            ce.setErr(err);
            //Execute and wait
            ce.open();
            Set<ClientChannelEvent> events =
                    ce.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), TimeUnit.SECONDS.toMillis(timeout));
            session.close(false);
            //Check if timed out
            if (events.contains(ClientChannelEvent.TIMEOUT)) {
                throw new RuntimeException(conn.getHostname()+" 命令 "+cmd+ "执行超时 "+timeout);
            }
            return new SshResponse(out.toString(), err.toString(), ce.getExitStatus());
        } finally {
            client.stop();
        }
    }
}

2.2.2.返回数据实体对象

/**
 * SSH应答结果集
 * @author lenny
 * @date 20230101
 */
@Data
public class SshResponse {
    private String stdOutput;
    private String errOutput;
    private int returnCode;

    public SshResponse(String stdOutput, String errOutput, int returnCode) {
        this.stdOutput = stdOutput;
        this.errOutput = errOutput;
        this.returnCode = returnCode;
    }
}

2.2.3.查询终端日志方法

这里使用Cat命令检索文件,并将检索到的文件内容进行处理

    /**
     * 查询终端日志
     * @param terminalNum 终端ID号
     * @param date 日期
     * @return
     */
    public SshResponse queryTerminalLog(String terminalNum, String date){
        try {
            Integer protocolType=808;
            TerminalInfo terminalInfo=terminalDao.queryTerminalInfoByTerminalNum(terminalNum);
            if(terminalInfo!=null&&terminalInfo.getProtocolType()>0){
                protocolType=terminalInfo.getProtocolType();
            }
            SshConnection conn  = new SshConnection(userName,password,host);
            String filter=String.format("grep -E '%s|%s' | grep '%s'", "received:", "downlink command:", terminalNum);
            String command=String.format("cat %s | %s",getTerminalLogPath(protocolType,date),filter);
            SshResponse sshResponse = SSHLinuxUtils.runCommand(conn,command,60);
            handleTerminalLogResponse(sshResponse);
            return sshResponse;
        } catch (Exception e) {
            log.error("queryTerminalLog:terminalId:{},date:{}",terminalNum,date,e);
            return null;
        }
    }


    /**
     * 处理返回的终端日志
     * @param sshResponse
     */
    private void handleTerminalLogResponse(SshResponse sshResponse){
        if(sshResponse.getReturnCode()==0&& StringUtils.isNotBlank(sshResponse.getStdOutput())){
            String terminalLog=sshResponse.getStdOutput();
            String [] terminalLogs = terminalLog.split("\\n");
            List<String> stringList=new ArrayList<>();
            for (String item : terminalLogs) {
                String regex;
                if (item.contains("downlink command:")) {
                    regex ="(?<= INFO).*?(?=downlink command:)";
                } else {
                    regex ="(?<= INFO).*?(?=received:)";
                }
                String result =item.replaceAll(regex,"").replace("INFO","")
                        .replace("downlink command","<<==")
                        .replace("received","==>>");
                stringList.add(result.toUpperCase());
            }
            sshResponse.setStdOutput(JSON.toJSONString(stringList));
        }
    }

2.2.4.封装Controller

    @PostMapping(value = "queryTerminalLog" )
    public Result<Object> queryTerminalLog(@RequestBody SshParams sshParams){
        if(sshParams==null||StringUtils.isBlank(sshParams.getTerminalNum())||StringUtils.isBlank(sshParams.getDate())){
            return ResultGenerator.failure(ResultEnum.PARAM_ERROR);
        }
        SshResponse response=sshService.queryTerminalLog(sshParams.getTerminalNum(),sshParams.getDate());
        if(response==null){
            return ResultGenerator.failure();
        }else {
            if (response.getReturnCode() > 0) {
                return ResultGenerator.failure(response.getErrOutput());
            } else {
                return ResultGenerator.success(response.getStdOutput());
            }
        }
    }

里面的方法包含了我系统使用中的一些实体类,这里并未给出,需要针对自己的项目情况做些调整。

3.结束语

对物联网软件感兴趣的朋友,可以加我QQ:571521973。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SpringBoot 集成Shell命令实现系统日志查询并通过API返回给前端 的相关文章

  • Python + Requests 模拟登陆(含验证码)

    其实模拟登陆非常简单 xff0c 只要在打开网站的同时提交数据就可以了 下面通过登陆超星网来举例说明如何一步步实现模拟登陆 1 获取需要提交的数据 使用chrome的Network或者fiddler可以很轻易的得到我们想要的数据 xff0c
  • Cmake实现递归cpp和h

    为解决获取编译链所有C 43 43 源文件和头文件 Cmake实现递归目录 编程心得 拾随小笺
  • 鉴权 前后端常见的几种鉴权方式

    https juejin cn post 6844903927100473357 鉴权 xff08 authentication xff09 是指验证用户是否拥有访问系统的权利 传统的鉴权是通过密码来验证的 这种方式的前提是 xff0c 每
  • curl指令模拟postman发json数据,发本地文件

    菜鸟curl指令介绍 xff1a https www coonote com linux linux cmd curl html post formdata多个参数 多个参数可以使用 F进行串接 curl span class token
  • 最全的HTTP(get post)请求示例, 包括post模拟get请求

    public class HttpRequest private static SimpleDateFormat sdf 61 new SimpleDateFormat 34 yyyyMMddHHmmss 34 private static
  • python爬虫——模拟登陆

    参考链接 xff1a https blog csdn net weixin 39875941 article details 109878457 模拟登陆 Python网络爬虫应用十分广泛 xff0c 但是有些网页需要用户登陆后才能获取到信
  • vector数组 传递 引用 指针 参数

    一 一维 span class hljs stl container span class hljs built in vector span lt span class hljs keyword int span gt span vec
  • Oracle # 字符串匹配函数(Oracle、SQLSERVER、Excel)

    引言 xff1a 当数据库设置字段的时候 xff0c 会设置1表XXX xff1b 0表示XXX 查询的时候怎么显示汉字呢 xff1f Oracle数据库 xff1a 普通查询数据 xff1a select from U ORANGEZAT
  • 时间序列预测——Prophet模型

    文章链接 xff1a 时间序列预测 ARIMA模型 https blog csdn net beiye article details 123317316 spm 61 1001 2014 3001 5502 1 Propht模型概述 Pr
  • 机器人导航——路径跟踪

    要完成一套完整的机器人路径规划 xff0c 并完成其物理实验并非一件简单的事情 参考 xff1a http wenku baidu com link url 61 n11mP6EDlM78NZYZ4yQYXzmzPeBV6BeLNOUjIv
  • python 读取txt出现\xef\xbb\xbf…的问题

    用python读取txt文件 xff0c 文件的内容是一列数如下 xff1a 1883 1886 1900 1900 1897 1897 1897 1897 1906 1917 1910 1910 但是读取的时候第一个元素为 xef xbb
  • (算法)判断两个区间是否重叠

    题目 xff1a 判断两个区间是否重叠 思路 xff1a 假设区间表示为 start end xff0c 先存在两个区间A B 两个区间的关系有两种 xff1a 重叠与不重叠 重叠的情况有4种 xff0c 两种相交 xff0c 两种包含 x
  • python ctrl+c 退出while True:

    写了一个死循环 xff0c 类似 xff1a def function while True my code 程序运行后想用ctrl 43 c按键停止程序 xff0c 可是终止不了 以下为解决办法 xff1a 第一步 xff1a 加入sys
  • python二维字典

    感谢原文 xff1a http www jb51 net article 83108 htm 本文实例讲述了Python的 二维 字典 two dimension dictionary 定义与实现方法 分享给大家供大家参考 xff0c 具体
  • ros安装出现依赖问题

    http www liuxiao org 2015 10 ros E5 9C A8 ubuntu 14 04 E7 B3 BB E7 BB 9F E4 B8 8A E5 AE 89 E8 A3 85 ros indigo 0 安装环境 xf
  • KMP算法介绍

    参考 xff1a https www cnblogs com c cloud p 3224788 html 前言 之前对kmp算法虽然了解它的原理 xff0c 即求出P0 Pi的最大相同前后缀长度k xff1b 但是问题在于如何求出这个最大
  • 【python】*与** 参数问题

    原文地址 xff1a https www cnblogs com paulwinflo p 5764748 html 可变参数 在Python函数中 xff0c 还可以定义可变参数 顾名思义 xff0c 可变参数就是传入的参数个数是可变的
  • 计算机网络编程知识总结

    博客出处 xff1a http www cnblogs com maybe2030 阅读目录 1 网络层次划分 2 OSI七层网络模型 3 IP地址 4 子网掩码及网络划分 5 ARP RARP协议 6 路由选择协议 7 TCP IP协议
  • 机器学习——决策树(decision tree)

    相关文章链接 xff1a 机器学习 人工神经网络 xff08 NN xff09 机器学习 卷积神经网络 xff08 CNN xff09 机器学习 循环神经网络 xff08 RNN xff09 机器学习 长短期记忆 xff08 LSTM xf
  • 基于UDP的Winsock编程(C++版)

    基于UDP的Winsock编程与基于TCP的Winsock编程相比 xff0c 只是缺少了一个步骤而已 对于Server xff0c 缺少了接受连接的过程 xff08 accept 函数调用 xff09 xff1b 对于Client xff

随机推荐