Jenkins Pipeline并行解析日志文件及trim()报错解决
问题描述
在Jenkins Pipeline中尝试并行执行耗时约1小时的script.sh并实时解析其日志,已编写对应Bash脚本和Pipeline代码,但运行时触发trim()方法报错,具体错误信息如下:
Also: org.jenkinsci.plugins.workflow.actions.ErrorAction$ErrorId: bd5e3fed-a88b-4ea2-ac3e-7a8e799a30f1 hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: java.lang.Integer.trim() is applicable for argument types: () values: [] Possible solutions: wait(), grep(), wait(long), grep(java.lang.Object), times(groovy.lang.Closure), is(java.lang.Object) at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onMethodCall(SandboxInterceptor.java:159)
相关代码
script.sh
#!/bin/bash # 定义ANSI颜色码 RED="\033[0;31m" GREEN="\033[0;32m" YELLOW="\033[0;33m" BLUE="\033[0;34m" RESET="\033[0m" # 打印带颜色文本的函数 print_colored_text() { local color="$1" local text="$2" echo -e "${color}${text}${RESET}" } # 打印不同颜色的文本 print_colored_text "$RED" "This is red text." print_colored_text "$GREEN" "This is green text." print_colored_text "$YELLOW" "This is yellow text." print_colored_text "$BLUE" "This is blue text." # 打印带颜色的提示信息 echo -e "${RED}Error:${RESET} Something went wrong." echo -e "${GREEN}Success:${RESET} Operation completed successfully." echo -e "${YELLOW}Warning:${RESET} This action is irreversible." echo -e "${BLUE}Info:${RESET} Please read the documentation." # 重置颜色设置 echo -e "${RESET}"
Jenkins Pipeline 原代码
pipeline { agent any stages { stage('Execute Bash Script') { steps { script { // 启动长耗时脚本并将输出重定向到文件 sh(script: 'chmod +x script.sh && ./script.sh > script_output.log &', returnStatus: true) def process = sh(script: 'tail -f script_output.log & echo $!', returnStatus: true, returnStdout: true).trim() echo "ON Next One" def pid = process.stdout.trim() echo "Next one again" while (true) { def status = sh(script: "ps -p $process -o etime=", returnStatus: true, returnStdout: true).stdout.trim() if (status.empty() || status.startsWith('+')) { // 脚本已完成或日志文件停止更新 break } sleep 30 // 根据需要调整休眠时长 } // 终止tail进程 sh(script: "kill $pid", returnStatus: true) // 检查脚本输出 def scriptOutput = readFile('script_output.log') if (scriptOutput.contains("This is red text.")) { echo "Parsing completed successfully." } else { echo "Parsing failed." } } } } } }
解决方案
报错原因分析
报错核心是**trim()方法被错误调用在了Integer类型变量上**:
- 当
sh命令同时设置returnStatus: true和returnStdout: true时,返回的是一个包含status(整数)和stdout(字符串)的复合对象,而非直接返回标准输出字符串。 - 原代码中
def process = sh(...).trim()错误地对这个复合对象调用trim(),后续又错误地用process作为PID执行ps命令,导致类型混乱触发报错。
修复后的Pipeline代码
pipeline { agent any stages { stage('Execute Bash Script') { steps { script { // 启动后台脚本并将输出写入日志文件 sh(script: 'chmod +x script.sh && ./script.sh > script_output.log &', returnStatus: true) // 获取tail进程的PID:直接取标准输出并去除首尾空白 def tailOutput = sh(script: 'tail -f script_output.log & echo $!', returnStdout: true).trim() def tailPid = tailOutput echo "Tail进程PID: ${tailPid}" // 轮询检查目标脚本是否仍在运行 while (true) { // 查找script.sh的进程状态,避免误查tail进程 def scriptStatus = sh(script: "pgrep -f script.sh || echo ''", returnStdout: true).trim() if (scriptStatus.isEmpty()) { // 目标脚本已结束,退出循环 break } // 实时解析日志内容(可根据需求添加自定义解析逻辑) def currentLog = readFile('script_output.log') if (currentLog.contains("Error:")) { echo "检测到错误日志" // 可根据需求添加告警或终止流程逻辑 } sleep 30 // 调整轮询间隔 } // 终止tail进程 sh(script: "kill ${tailPid}", returnStatus: true) // 最终检查日志结果 def finalOutput = readFile('script_output.log') if (finalOutput.contains("This is red text.")) { echo "日志解析完成,结果正常。" } else { echo "日志解析失败。" } } } } } }
关键修复点
- 修正
sh命令返回值处理:去掉returnStatus: true,直接获取标准输出字符串并调用trim(),避免对复合对象操作。 - 正确监控目标脚本状态:用
pgrep -f script.sh替代检查tail进程,确保监控的是实际运行的目标脚本而非日志监听进程。 - 优化实时解析逻辑:在轮询过程中直接读取日志文件并执行解析,实现真正的实时监控。
替代方案:使用Parallel步骤实现并行执行
如果需要更简洁可靠的并行任务和日志处理,可使用Jenkins的Parallel步骤同时运行脚本和日志解析:
pipeline { agent any stages { stage('Parallel Execution & Log Parsing') { steps { script { // 运行目标脚本的闭包 def scriptRun = { sh './script.sh' } // 实时解析日志的闭包 def logParse = { sleep 10 // 等待脚本启动生成日志 while (true) { if (fileExists('script_output.log')) { def logContent = readFile('script_output.log') // 自定义实时解析逻辑 if (logContent.contains("Success:")) { echo "实时检测到成功日志" } } // 检查目标脚本是否仍在运行 def isRunning = sh(script: "pgrep -f script.sh || echo 0", returnStdout: true).trim() if (isRunning == '0') break sleep 30 } } // 并行执行脚本运行和日志解析任务 parallel( "Run Script": scriptRun, "Parse Log": logParse ) } } } } }
内容的提问来源于stack exchange,提问作者Neetesshhr
相关产品推荐
相关产品推荐

