jsr223全局监听器,判断响应断言不满足条件时,写日记到同级jmx同级目录,方便取回日记
一个取样器的全局结果基本是ok的,可以避免持续error,减少磁盘压力
全局时多个取样器可能有1个取样器占满,就不再打印其他日志了,可放在每个取样器的局部变量避免
/**
* JMeter 错误日志记录
*/
import org.apache.jmeter.services.FileServer
import java.text.SimpleDateFormat
// --- 1. 配置参数 ---
def MAX_LOG_ENTRIES = 500 // 单次压测最多记录多少条错误日志
def MAX_BODY_LENGTH = 1024 // 响应体记录的最大字符数
def ERROR_SAMPLING_RATE = 1.0// 错误记录采样率 (1.0表示所有错误, 0.1表示只随机记录10%的错误)
// --- 2. 状态校验检查 ---
if (prev == null || prev.isSuccessful()) return
def globalErrorCount = props.get("GLOBAL_ERR_COUNT") ?: 0
if (globalErrorCount >= MAX_LOG_ENTRIES) return
if (Math.random() > ERROR_SAMPLING_RATE) return
// --- 3. 准备数据
def truncateAndClean = { str, limit ->
if (!str) return "N/A"
// 先截,减少正则处理内存压力
def shortStr = str.length() > limit ? str.substring(0, limit) : str
// 避免字符串转义报错
return shortStr.replaceAll(/\s+/, " ").trim() + (str.length() > limit ? " [已截断]" : "")
}
def now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())
def reqBody = truncateAndClean(prev.getSamplerData(), MAX_BODY_LENGTH)
def respBody = truncateAndClean(prev.getResponseDataAsString(), MAX_BODY_LENGTH)
// --- 4. 构建日志
def logEntry = [
"\n[${now}] [#${globalErrorCount + 1}] x ${prev.getSampleLabel()}",
"URL: ${prev.getUrlAsString()}",
"Status: ${prev.getResponseCode()}",
"Request: ${reqBody}",
"Response: ${respBody}",
"------------------------------------------------------------"
].join("\n")
// --- 5. 线程安全写入 ---
try {
def fs = FileServer.getFileServer()
def baseDir = fs.getBaseDir()
if (baseDir == null) return // 脚本未保存时跳过
def logFile = new File(baseDir, "jmeter_errors_protected.log")
synchronized (FileServer.class) {
def currentCount = props.get("GLOBAL_ERR_COUNT") ?: 0
if (currentCount < MAX_LOG_ENTRIES) {
logFile.append(logEntry + "\n", "UTF-8")
props.put("GLOBAL_ERR_COUNT", currentCount + 1)
}
}
} catch (Exception e) {
log.error("写入错误日志失败: " + e.getMessage())
}