This is an automated email from the ASF dual-hosted git repository.

gongchao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git


The following commit(s) were added to refs/heads/master by this push:
     new 348b50c4c [feature] Optimize script protocol and add Windows-script 
yml template (#2367)
348b50c4c is described below

commit 348b50c4c6491a06c7c108f89e49b3821cc87965
Author: Zhang Yuxuan <[email protected]>
AuthorDate: Wed Jul 24 23:38:10 2024 +0800

    [feature] Optimize script protocol and add Windows-script yml template 
(#2367)
    
    Signed-off-by: Zhang Yuxuan <[email protected]>
    Co-authored-by: tomsun28 <[email protected]>
---
 .../collect/script/ScriptCollectImpl.java          |  51 +++-
 .../common/entity/job/protocol/ScriptProtocol.java |   5 +
 .../src/main/resources/define/app-linux_script.yml |   9 +
 .../main/resources/define/app-windows_script.yml   | 334 +++++++++++++++++++++
 4 files changed, 383 insertions(+), 16 deletions(-)

diff --git 
a/collector/src/main/java/org/apache/hertzbeat/collector/collect/script/ScriptCollectImpl.java
 
b/collector/src/main/java/org/apache/hertzbeat/collector/collect/script/ScriptCollectImpl.java
index 10dbe0096..aa7dc0674 100644
--- 
a/collector/src/main/java/org/apache/hertzbeat/collector/collect/script/ScriptCollectImpl.java
+++ 
b/collector/src/main/java/org/apache/hertzbeat/collector/collect/script/ScriptCollectImpl.java
@@ -46,10 +46,13 @@ import org.springframework.util.StringUtils;
 public class ScriptCollectImpl extends AbstractCollect {
     public static final String WINDOWS_SCRIPT = "windows_script";
     public static final String LINUX_SCRIPT = "linux_script";
-    private static final String CMD = "cmd.exe";
+    private static final String CMD = "cmd";
     private static final String BASH = "bash";
+    private static final String POWERSHELL = "powershell";
     private static final String CMD_C = "/c";
     private static final String BASH_C = "-c";
+    private static final String POWERSHELL_C = "-Command";
+    private static final String POWERSHELL_FILE = "-File";
     private static final String PARSE_TYPE_ONE_ROW = "oneRow";
     private static final String PARSE_TYPE_MULTI_ROW = "multiRow";
     private static final String PARSE_TYPE_NETCAT = "netcat";
@@ -62,6 +65,7 @@ public class ScriptCollectImpl extends AbstractCollect {
         Assert.notNull(scriptProtocol, "Script collect must has Imap params");
         Assert.notNull(scriptProtocol.getCharset(), "Script charset is 
required");
         Assert.notNull(scriptProtocol.getParseType(), "Script parse type is 
required");
+        Assert.notNull(scriptProtocol.getScriptTool(), "Script tool is 
required");
         if (!(StringUtils.hasText(scriptProtocol.getScriptCommand()) || 
StringUtils.hasText(scriptProtocol.getScriptPath()))) {
             throw new IllegalArgumentException("At least one script command or 
script path is required.");
         }
@@ -72,35 +76,50 @@ public class ScriptCollectImpl extends AbstractCollect {
         ScriptProtocol scriptProtocol = metrics.getScript();
         long startTime = System.currentTimeMillis();
         ProcessBuilder processBuilder;
-        if (WINDOWS_SCRIPT.equalsIgnoreCase(app)) {
-            if (StringUtils.hasText(scriptProtocol.getScriptCommand())) {
-                processBuilder = new ProcessBuilder(CMD, CMD_C, 
scriptProtocol.getScriptCommand().trim());
-            } else {
-                processBuilder = new ProcessBuilder(CMD, 
scriptProtocol.getScriptPath().trim());
+        // use command
+        if (StringUtils.hasText(scriptProtocol.getScriptCommand())) {
+            switch (scriptProtocol.getScriptTool()) {
+                case BASH -> processBuilder = new ProcessBuilder(BASH, BASH_C, 
scriptProtocol.getScriptCommand().trim());
+                case CMD -> processBuilder = new ProcessBuilder(CMD, CMD_C, 
scriptProtocol.getScriptCommand().trim());
+                case POWERSHELL -> processBuilder = new 
ProcessBuilder("powershell.exe", POWERSHELL_C, 
scriptProtocol.getScriptCommand().trim());
+                default -> {
+                    builder.setCode(CollectRep.Code.FAIL);
+                    builder.setMsg("Not support script tool:" + 
scriptProtocol.getScriptTool());
+                    return;
+                }
             }
-
-        } else if (LINUX_SCRIPT.equalsIgnoreCase(app)) {
-            if (StringUtils.hasText(scriptProtocol.getScriptCommand())) {
-                processBuilder = new ProcessBuilder(BASH, BASH_C, 
scriptProtocol.getScriptCommand().trim());
-            } else {
-                processBuilder = new ProcessBuilder(BASH, 
scriptProtocol.getScriptPath().trim());
+        // use command file
+        } else if (StringUtils.hasText((scriptProtocol.getScriptPath()))) {
+            switch (scriptProtocol.getScriptTool()) {
+                case BASH -> processBuilder = new ProcessBuilder(BASH, 
scriptProtocol.getScriptPath().trim());
+                case CMD -> processBuilder = new ProcessBuilder(CMD,  
scriptProtocol.getScriptPath().trim());
+                case POWERSHELL -> processBuilder = new 
ProcessBuilder(POWERSHELL, POWERSHELL_FILE, 
scriptProtocol.getScriptPath().trim());
+                default -> {
+                    builder.setCode(CollectRep.Code.FAIL);
+                    builder.setMsg("Not support script tool:" + 
scriptProtocol.getScriptTool());
+                    return;
+                }
             }
         } else {
             builder.setCode(CollectRep.Code.FAIL);
-            builder.setMsg("Not support OS:" + app);
+            builder.setMsg("At least one script command or script path is 
required.");
             return;
         }
+        // set work directory
         String workDirectory = scriptProtocol.getWorkDirectory();
         if (StringUtils.hasText(workDirectory)) {
             processBuilder.directory(new File(workDirectory));
         }
+        // execute command
         try {
             Process process = processBuilder.start();
             BufferedReader reader = new BufferedReader(new 
InputStreamReader(process.getInputStream(), 
Charset.forName(scriptProtocol.getCharset())));
             StringBuilder response = new StringBuilder();
             String line;
             while ((line = reader.readLine()) != null) {
-                response.append(line).append("\n");
+                if (StringUtils.hasText(line)) {
+                    response.append(line).append("\n");
+                }
             }
             process.waitFor();
             Long responseTime = System.currentTimeMillis() - startTime;
@@ -224,13 +243,13 @@ public class ScriptCollectImpl extends AbstractCollect {
             log.error("ssh response data only has header: {}", result);
             return;
         }
-        String[] fields = lines[0].split(" ");
+        String[] fields = lines[0].split("\\s+");
         Map<String, Integer> fieldMapping = new HashMap<>(fields.length);
         for (int i = 0; i < fields.length; i++) {
             fieldMapping.put(fields[i].trim().toLowerCase(), i);
         }
         for (int i = 1; i < lines.length; i++) {
-            String[] values = lines[i].split(" ");
+            String[] values = lines[i].split("\\s+");
             CollectRep.ValueRow.Builder valueRowBuilder = 
CollectRep.ValueRow.newBuilder();
             for (String alias : aliasFields) {
                 if (CollectorConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
diff --git 
a/common/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/ScriptProtocol.java
 
b/common/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/ScriptProtocol.java
index 11079a1b8..1f116b30a 100644
--- 
a/common/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/ScriptProtocol.java
+++ 
b/common/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/ScriptProtocol.java
@@ -55,4 +55,9 @@ public class ScriptProtocol {
      * Response data parsing mode:oneRow, multiRow
      */
     private String parseType;
+
+    /**
+     * Script tool name, exp: bash, cmd, powershell
+     */
+    private String scriptTool;
 }
diff --git a/manager/src/main/resources/define/app-linux_script.yml 
b/manager/src/main/resources/define/app-linux_script.yml
index 9de6a45e7..b5d2633ed 100644
--- a/manager/src/main/resources/define/app-linux_script.yml
+++ b/manager/src/main/resources/define/app-linux_script.yml
@@ -74,6 +74,8 @@ metrics:
     protocol: script
     # the config content when protocol is ssh
     script:
+      # script tool
+      scriptTool: bash
       # OS charset
       charset: UTF-8
       # script working directory
@@ -139,6 +141,7 @@ metrics:
       - usage=100-idle
     protocol: script
     script:
+      scriptTool: bash
       charset: UTF-8
       scriptCommand:
         |
@@ -202,6 +205,7 @@ metrics:
       - usage=(used / total) * 100
     protocol: script
     script:
+      scriptTool: bash
       charset: UTF-8
       scriptCommand: free -m | awk 'BEGIN{print "total used free buff_cache 
available"} NR==2{print $2,$3,$4,$6,$7}'
       parseType: multiRow
@@ -240,6 +244,7 @@ metrics:
           en-US: Write Rate
     protocol: script
     script:
+      scriptTool: bash
       charset: UTF-8
       scriptCommand: vmstat -D | awk 'NR==1{print $1}';vmstat -D | awk 
'NR==2{print $1}';vmstat 1 1 | awk 'NR==3{print $10}';vmstat 1 1 | awk 
'NR==3{print $9}';vmstat 1 1 | awk 'NR==3{print $16}'
       parseType: oneRow
@@ -273,6 +278,7 @@ metrics:
       - transmit_bytes=B->MB
     protocol: script
     script:
+      scriptTool: bash
       charset: UTF-8
       scriptCommand: cat /proc/net/dev | tail -n +3 | awk 'BEGIN{ print 
"interface_name receive_bytes transmit_bytes"} {print $1,$2,$10}'
       parseType: multiRow
@@ -314,6 +320,7 @@ metrics:
           en-US: Mounted
     protocol: script
     script:
+      scriptTool: bash
       charset: UTF-8
       scriptCommand: df -mP | tail -n +2 | awk 'BEGIN{ print "filesystem used 
available usage mounted"} {print $1,$3,$4,$5,$6}'
       parseType: multiRow
@@ -349,6 +356,7 @@ metrics:
           en-US: Command
     protocol: script
     script:
+      scriptTool: bash
       charset: UTF-8
       scriptCommand: ps aux | sort -k3nr | awk 'BEGIN{ print "pid cpu_usage 
mem_usage command" } {printf "%s %s %s ", $2, $3, $4; for (i=11; i<=NF; i++) { 
printf "%s", $i; if (i < NF) printf " "; } print ""}' | head -n 11
       parseType: multiRow
@@ -384,6 +392,7 @@ metrics:
           en-US: Command
     protocol: script
     script:
+      scriptTool: bash
       charset: UTF-8
       scriptCommand: ps aux | sort -k4nr | awk 'BEGIN{ print "pid cpu_usage 
mem_usage command" } {printf "%s %s %s ", $2, $3, $4; for (i=11; i<=NF; i++) { 
printf "%s", $i; if (i < NF) printf " "; } print ""}' | head -n 11
       parseType: multiRow
diff --git a/manager/src/main/resources/define/app-windows_script.yml 
b/manager/src/main/resources/define/app-windows_script.yml
new file mode 100644
index 000000000..a13e9e248
--- /dev/null
+++ b/manager/src/main/resources/define/app-windows_script.yml
@@ -0,0 +1,334 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The monitoring type category:service-application service monitoring 
db-database monitoring custom-custom monitoring os-operating system monitoring
+category: os
+# The monitoring type eg: linux windows tomcat mysql aws...
+app: windows_script
+# The monitoring i18n name
+name:
+  zh-CN: Windows 命令
+  en-US: Windows Script
+# The description and help of this monitoring type
+help:
+  zh-CN: Hertzbeat 使用采集器作为 agent 直接运行 <a class='help_module_content' 
href='https://hertzbeat.apache.org/docs/advanced/extend-script'> CMD 或 
Powershell 命令 </a> 对 Windows 操作系统的通用性能指标 (系统信息、CPU、内存、磁盘、网卡、文件系统、TOP资源进程等) 
进行采集监控。<br>您可以点击“<i>新建 Linux Script</i>”进行添加。或者选择“<i>更多操作</i>”,导入已有配置。
+  en-US: Hertzbeat uses a collector as an agent to directly execute <a 
class='help_module_content' 
href='https://hertzbeat.apache.org/docs/advanced/extend-script'> CMD or 
Powershell commands </a> to collect and monitor general performance metrics of 
the Windows operating system (system information, CPU, memory, disk, network 
card, file system, TOP resource processes, etc.).<br>You can click “<i>Create 
New Linux Script</i>” to add it. Or select “<i>More Actions</i>” to import an 
existing  [...]
+  zh-TW: Hertzbeat 使用采集器作為 agent 直接運行 <a class='help_module_content' 
href='https://hertzbeat.apache.org/docs/advanced/extend-script'> CMD 或 
Powershell 命令 </a> 對 Windows 操作系統的通用性能指標 (系統信息、CPU、內存、磁盤、網卡、文件系統、TOP資源進程等) 
進行採集監控。<br>您可以點擊“<i>新建 Linux Script</i>”進行添加。或者選擇“<i>更多操作</i>”,導入已有配置。
+helpLink:
+  zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/script
+  en-US: https://hertzbeat.apache.org/docs/help/script
+# Input params define for monitoring(render web ui by the definition)
+params:
+  # field-param field key
+  - field: host
+    # name-param field display i18n name
+    name:
+      zh-CN: 目标Host
+      en-US: Target Host
+    # type-param field type(most mapping the html input type)
+    type: host
+    # required-true or false
+    required: true
+# collect metrics config list
+metrics:
+  # metrics - basic, inner monitoring metrics (responseTime - response time)
+  - name: basic
+    i18n:
+      zh-CN: 系统基本信息
+      en-US: Basic Info
+    # metrics scheduling priority(0->127)->(high->low), metrics with the same 
priority will be scheduled in parallel
+    # priority 0's metrics is availability metrics, it will be scheduled 
first, only availability metrics collect success will the scheduling continue
+    priority: 0
+    # collect metrics content
+    fields:
+      # field-metric name, type-metric type(0-number,1-string), unit-metric 
unit('%','ms','MB'), label-whether it is a metrics label field
+      - field: hostname
+        type: 1
+        label: true
+        i18n:
+          zh-CN: 主机名称
+          en-US: Host Name
+      - field: version
+        type: 1
+        i18n:
+          zh-CN: 操作系统版本
+          en-US: System Version
+    # the protocol used for monitoring, eg: sql, ssh, http, telnet, wmi, snmp, 
sdk
+    protocol: script
+    # the config content when protocol is ssh
+    script:
+      # script tool
+      scriptTool: cmd
+      # OS charset
+      charset: GBK
+      # script working directory
+      workDirectory: "C:"
+      # collect script
+      scriptCommand:
+        |
+        @echo %COMPUTERNAME% & for /f "tokens=4 delims=[] " %i in ('ver') do 
@echo %i
+      # response data parse type: oneRow, multiRow
+      parseType: oneRow
+
+  - name: cpu
+    i18n:
+      zh-CN: CPU 信息
+      en-US: CPU Info
+    priority: 1
+    fields:
+      - field: info
+        type: 1
+        i18n:
+          zh-CN: 型号
+          en-US: Info
+      - field: cores
+        type: 1
+        i18n:
+          zh-CN: 核数
+          en-US: Cores
+      - field: load
+        type: 1
+        unit: '%'
+        i18n:
+          zh-CN: 负载
+          en-US: Load
+      - field: interrupt
+        type: 1
+        i18n:
+          zh-CN: 中断数
+          en-US: Interrupt
+      - field: context_switch
+        type: 1
+        i18n:
+          zh-CN: 上下文切换
+          en-US: Context Switch
+      - field: usage
+        type: 1
+        unit: '%'
+        i18n:
+          zh-CN: 使用率
+          en-US: Usage
+    protocol: script
+    script:
+      scriptTool: cmd
+      charset: GBK
+      scriptCommand:
+        |
+        for /f "tokens=2 delims==" %i in ('wmic cpu get Name /value ^| find 
"Name"') do @echo %i&&for /f "tokens=2 delims==" %i in ('wmic cpu get 
NumberOfLogicalProcessors /value ^| find "NumberOfLogicalProcessors"') do @echo 
%i&&for /f "tokens=2 delims==" %i in ('wmic cpu get loadpercentage /value') do 
@echo %i&&for /f "skip=2 tokens=2 delims=," %a in ('typeperf "\Processor 
Information(_Total)\Interrupts/sec" -sc 1') do @echo %a && for /f "skip=2 
tokens=2 delims=," %a in ('typeperf "\Sy [...]
+      parseType: oneRow
+
+  - name: memory
+    i18n:
+      zh-CN: 内存信息
+      en-US: Memory Info
+    priority: 1
+    fields:
+      - field: totalPhysical
+        type: 0
+        unit: Mb
+        i18n:
+          zh-CN: 总物理内存容量
+          en-US: Total Physical Memory
+      - field: freePhysical
+        type: 0
+        unit: Mb
+        i18n:
+          zh-CN: 空闲物理内存容量
+          en-US: Free Physical Memory
+      - field: totalVirtual
+        type: 0
+        unit: Mb
+        i18n:
+          zh-CN: 总虚拟内存容量
+          en-US: Total Virtual Memory
+      - field: freeVirtual
+        type: 0
+        unit: Mb
+        i18n:
+          zh-CN: 空闲虚拟内存容量
+          en-US: Free Virtual Memory
+    units:
+      - totalPhysical=B->MB
+      - freePhysical=KB->MB
+      - totalVirtual=KB->MB
+      - freeVirtual=KB->MB
+    protocol: script
+    script:
+      scriptTool: cmd
+      charset: GBK
+      scriptCommand:
+        |
+        for /f "tokens=2 delims==" %i in ('wmic computerSystem get 
TotalPhysicalMemory /value ^| find "TotalPhysicalMemory"') do @echo %i&&for /f 
"tokens=2 delims==" %i in ('wmic os get FreePhysicalMemory /value ^| find 
"FreePhysicalMemory"') do @echo %i&&for /f "tokens=2 delims==" %i in ('wmic os 
get TotalVirtualMemorySize /value ^| find "TotalVirtualMemorySize"') do @echo 
%i&&for /f "tokens=2 delims==" %i in ('wmic os get FreeVirtualMemory /value ^| 
find "FreeVirtualMemory"') do @echo %i
+      parseType: oneRow
+
+  - name: disk
+    i18n:
+      zh-CN: 磁盘信息
+      en-US: Disk Info
+    priority: 1
+    fields:
+      - field: Model
+        type: 1
+        i18n:
+          zh-CN: 磁盘型号
+          en-US: Disk Type
+      - field: Size
+        type: 0
+        unit: Mb
+        i18n:
+          zh-CN: 磁盘大小
+          en-US: Disk Size
+      - field: BytesPerSector
+        type: 0
+        i18n:
+          zh-CN: 每个扇区的字节数
+          en-US: Bytes Per Sector
+    protocol: script
+    script:
+      scriptTool: powershell
+      charset: GBK
+      scriptCommand:
+        |
+        Write-Host "Model Size BytesPerSector";
+        Get-WmiObject -Class Win32_DiskDrive | ForEach-Object {
+            $model = $_.Model -replace ' ', '_'
+            $size = [math]::round($_.Size / 1MB, 0)
+            $bytesPerSector = $_.BytesPerSector
+            Write-Host "$model $size $bytesPerSector"
+        }
+      parseType: multiRow
+
+  - name: disk_free
+    i18n:
+      zh-CN: 文件系统
+      en-US: Disk Free
+    priority: 1
+    fields:
+      - field: Caption
+        type: 1
+        i18n:
+          zh-CN: 盘符
+          en-US: Caption
+      - field: FreeSpace
+        type: 0
+        unit: Mb
+        i18n:
+          zh-CN: 可用量
+          en-US: FreeSpace
+      - field: Size
+        type: 0
+        unit: Mb
+        i18n:
+          zh-CN: 总量
+          en-US: Size
+    units:
+      - FreeSpace=B->MB
+      - Size=B->MB
+    protocol: script
+    script:
+      scriptTool: cmd
+      charset: GBK
+      scriptCommand:
+        |
+        wmic logicaldisk get size,freespace,caption
+      parseType: multiRow
+
+  - name: top_cpu_process
+    i18n:
+      zh-CN: Top10 CPU 进程
+      en-US: Top10 CPU Process
+    priority: 1
+    fields:
+      - field: name
+        type: 1
+        i18n:
+          zh-CN: 进程名
+          en-US: Name
+      - field: id
+        type: 1
+        i18n:
+          zh-CN: 进程 ID
+          en-US: PID
+      - field: cpu
+        type: 0
+        unit: 's'
+        i18n:
+          zh-CN: CPU 使用时间
+          en-US: CPU Usage Time
+      - field: ws
+        type: 0
+        unit: Mb
+        i18n:
+          zh-CN: 内存占用量
+          en-US: Memory Usage
+    protocol: script
+    script:
+      scriptTool: powershell
+      charset: GBK
+      scriptCommand:
+        |
+        Write-Host "name id cpu ws";
+        Get-Process | Sort-Object -Property CPU -Descending | Select-Object 
-First 10 | ForEach-Object {
+          $name = $_.Name -replace ' ', '_'
+          $id = $_.Id
+          $cpu = [math]::round($_.CPU, 0)
+          $ws = [math]::round($_.WS / 1MB, 0)
+          Write-Host "$name $id $cpu $ws"
+        }
+      parseType: multiRow
+
+  - name: top_mem_process
+    i18n:
+      zh-CN: Top10 内存进程
+      en-US: Top10 Memory Process
+    priority: 1
+    fields:
+      - field: name
+        type: 1
+        i18n:
+          zh-CN: 进程名称
+          en-US: Process Name
+      - field: id
+        type: 1
+        i18n:
+          zh-CN: 进程 ID
+          en-US: PID
+      - field: ws
+        type: 0
+        unit: Mb
+        i18n:
+          zh-CN: 内存占用量
+          en-US: Memory Usage
+      - field: cpu
+        type: 0
+        unit: s
+        i18n:
+          zh-CN: CPU 占用时间
+          en-US: CPU Usage Time
+    protocol: script
+    script:
+      scriptTool: powershell
+      charset: GBK
+      scriptCommand:
+        |
+        Write-Host "name id ws cpu";
+        Get-Process | Sort-Object -Property WS -Descending | Select-Object 
-First 10 | ForEach-Object {
+          $name = $_.Name -replace ' ', '_'
+          $id = $_.Id
+          $ws = [math]::round($_.WS / 1MB, 0)
+          $cpu = [math]::round($_.CPU, 0)
+          Write-Host "$name $id $ws $cpu"
+        }
+      parseType: multiRow
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to