本文目录导读:

Shell脚本 - 保留关键堆栈行
#!/bin/bash
# 文件名: stack_filter.sh
# 过滤堆栈,保留特定包路径的行
filter_stack() {
local input_file="${1:-/dev/stdin}"
local packages="${2:-com.example,org.springframework}"
# 将包名转为grep模式
local pattern=$(echo "$packages" | tr ',' '|')
awk -v pattern="$pattern" '
BEGIN {
# 分割设置
FS="\t"
show_line = 0
}
# 保留异常类型和消息
/^[a-zA-Z]+\.[a-zA-Z]+.*Exception/ {
print $0
show_line = 1
next
}
# 保留匹配的关键包堆栈
$0 ~ pattern && /at / {
if (show_line) {
print $0
show_line = 0
} else {
print $0
}
next
}
# 保留"Caused by"行
/^Caused by:/ {
print $0
show_line = 1
next
}
# 保留堆栈结束标记
/^\.\.\. [0-9]+ more/ {
print $0
show_line = 0
}
' "$input_file"
}
# 使用示例
cat << 'EOF' | filter_stack "" "com.example"
java.lang.NullPointerException
at com.example.service.UserService.getUser(UserService.java:25)
at com.example.controller.UserController.findById(UserController.java:12)
at com.example.config.AopConfig.invoke(AopConfig.java:45)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
... 23 more
EOF
Python脚本 - 智能堆栈合并
#!/usr/bin/env python3
# 文件名: stack_minifier.py
import sys
import re
from collections import defaultdict
class StackMinifier:
def __init__(self, packages_to_keep=None):
"""
packages_to_keep: 需要保留的包路径列表,如 ['com.example', 'org.springframework']
"""
self.packages_to_keep = packages_to_keep or ['com.example', 'org.springframework']
self.patterns = [re.compile(f'^{pkg}') for pkg in self.packages_to_keep]
def process_stacktrace(self, text):
"""处理堆栈信息"""
lines = text.split('\n')
result = []
i = 0
while i < len(lines):
line = lines[i]
# 保留异常信息行
if re.match(r'^[a-zA-Z]+\.[a-zA-Z]+.*Exception', line):
result.append(line)
i += 1
continue
# 处理at行
if 'at ' in line:
frame_info = self._extract_frame_info(line)
if self._should_keep(frame_info['class']):
result.append(line)
# 检查是否有连续的堆栈行
j = i + 1
skipped = 0
while j < len(lines) and 'at ' in lines[j]:
next_frame = self._extract_frame_info(lines[j])
if self._should_keep(next_frame['class']):
result.append(lines[j])
else:
skipped += 1
j += 1
if skipped > 0:
result.append(f" ... {skipped} more [filtered]")
i = j
continue
# 保留Caused by行
elif line.startswith('Caused by:'):
result.append(line)
i += 1
return '\n'.join(result)
def _extract_frame_info(self, line):
"""提取堆栈帧信息"""
# 匹配格式: at com.example.Class.method(File.java:line)
match = re.search(r'at\s+([\w.]+)\.(\w+)\(([^:]+):(\d+)\)', line)
if match:
return {
'full_class': match.group(1),
'class': match.group(1).rsplit('.', 1)[0] if '.' in match.group(1) else '',
'method': match.group(2),
'file': match.group(3),
'line': match.group(4)
}
return {'full_class': '', 'class': '', 'method': '', 'file': '', 'line': ''}
def _should_keep(self, class_name):
"""判断是否应该保留该堆栈帧"""
for pattern in self.patterns:
if pattern.search(class_name):
return True
return False
def main():
# 从命令行参数读取需要保留的包
import argparse
parser = argparse.ArgumentParser(description='精简Java异常堆栈信息')
parser.add_argument('file', nargs='?', help='输入文件路径,默认从标准输入读取')
parser.add_argument('--packages', '-p',
default='com.example,org.springframework',
help='需要保留的包前缀,逗号分隔')
args = parser.parse_args()
# 读取输入
if args.file:
with open(args.file, 'r') as f:
input_text = f.read()
else:
input_text = sys.stdin.read()
# 处理堆栈
packages = args.packages.split(',')
minifier = StackMinifier(packages)
output = minifier.process_stacktrace(input_text)
print(output)
if __name__ == '__main__':
main()
Java工具 - 运行时精简
import java.util.*;
import java.util.stream.*;
import java.util.function.Predicate;
public class StackTraceSimplifier {
private final Set<String> packagesToKeep;
public StackTraceSimplifier(String... packages) {
this.packagesToKeep = new HashSet<>(Arrays.asList(packages));
}
/**
* 简化异常堆栈
*/
public String simplify(Throwable throwable) {
StringBuilder sb = new StringBuilder();
simplifyThrowable(throwable, sb, 0);
return sb.toString();
}
private void simplifyThrowable(Throwable throwable, StringBuilder sb, int depth) {
if (throwable == null || depth > 3) { // 限制chained exception深度
return;
}
// 添加异常信息
sb.append(throwable.getClass().getName())
.append(": ")
.append(throwable.getMessage())
.append("\n");
// 过滤堆栈
List<StackTraceElement> filteredStack = Arrays.stream(throwable.getStackTrace())
.filter(this::shouldKeep)
.collect(Collectors.toList());
// 获取被过滤的数量
long filteredCount = throwable.getStackTrace().length - filteredStack.size();
// 输出过滤后的堆栈
for (StackTraceElement element : filteredStack) {
sb.append("\tat ")
.append(element.getClassName())
.append(".")
.append(element.getMethodName())
.append("(")
.append(element.getFileName())
.append(":")
.append(element.getLineNumber())
.append(")\n");
}
if (filteredCount > 0) {
sb.append("\t... ").append(filteredCount).append(" more [filtered]\n");
}
// 处理cause
if (throwable.getCause() != null) {
sb.append("Caused by: ");
simplifyThrowable(throwable.getCause(), sb, depth + 1);
}
}
private boolean shouldKeep(StackTraceElement element) {
String className = element.getClassName();
return packagesToKeep.stream().anyMatch(className::startsWith);
}
// 使用示例
public static void main(String[] args) {
try {
// 模拟异常
throw new RuntimeException("Test exception",
new IllegalArgumentException("Root cause"));
} catch (Exception e) {
StackTraceSimplifier simplifier =
new StackTraceSimplifier("com.example", "org.springframework");
System.out.println(simplifier.simplify(e));
}
}
}
Node.js/JavaScript 版本
#!/usr/bin/env node
// 文件名: stack-minifier.js
class StackMinifier {
constructor(options = {}) {
this.modulesToKeep = options.modules || ['app/', 'src/'];
this.maxDepth = options.maxDepth || 20;
}
/**
* 精简Node.js异常堆栈
*/
simplifyError(error) {
if (!error || !error.stack) return '';
const lines = error.stack.split('\n');
const result = [];
// 保留错误消息行
result.push(lines[0]);
// 过滤堆栈行
let currentDepth = 0;
let skippedCount = 0;
for (let i = 1; i < lines.length; i++) {
const line = lines[i].trim();
if (currentDepth >= this.maxDepth) {
skippedCount++;
continue;
}
// 检查是否是内部模块
if (this.shouldKeep(line)) {
result.push(' ' + line);
currentDepth++;
} else {
skippedCount++;
}
}
if (skippedCount > 0) {
result.push(` ... ${skippedCount} frames were omitted`);
}
return result.join('\n');
}
shouldKeep(stackLine) {
// 过滤node_modules和原生模块
if (stackLine.includes('node_modules/')) return false;
if (stackLine.includes('(native)')) return false;
if (stackLine.includes('internal/')) return false;
// 保留指定模块
return this.modulesToKeep.some(module => stackLine.includes(module));
}
}
// 命令行工具
const fs = require('fs');
if (process.argv.length > 2) {
const inputFile = process.argv[2];
const content = fs.readFileSync(inputFile, 'utf-8');
const minifier = new StackMinifier({
modules: process.argv[3]?.split(',') || ['app/', 'src/']
});
// 假设内容中可能包含多个异常堆栈
const stacks = content.split(/(?=\w+Error:)/);
stacks.forEach(stack => {
if (stack.trim()) {
console.log(minifier.simplifyError({ stack: stack.trim() }));
}
});
} else {
// 从stdin读取
const chunks = [];
process.stdin.on('data', chunk => chunks.push(chunk));
process.stdin.on('end', () => {
const content = Buffer.concat(chunks).toString();
const minifier = new StackMinifier();
console.log(minifier.simplifyError({ stack: content }));
});
}
使用建议
- 生产环境日志处理:结合日志收集系统使用
- 开发调试:保留完整堆栈,仅在输出时精简
- 监控告警:只保留关键业务包(如com.example)的堆栈
- 集成到构建工具:作为post-processing步骤
这些脚本可以根据你的具体需求调整过滤规则和输出格式。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播