如何提取日志错误聚类桶中所有字符串的公共部分并生成匹配的Glob/正则表达式?
如何提取日志错误聚类桶中所有字符串的公共部分并生成匹配的Glob/正则表达式?
看起来你已经把日志错误字符串聚类搞定了,现在想给每个聚类桶生成能匹配所有成员的通用Glob/正则模式——这个需求确实比只展示第一个字符串友好太多,能让用户一眼抓住错误的共性!
一、复用你已有的difflib工具链
你已经在用difflib.SequenceMatcher做聚类,其实可以复用它的匹配功能来生成公共模式。SequenceMatcher的get_matching_blocks()方法能返回两个字符串中连续匹配的区间,我们可以基于这个来构建两两字符串的公共模式,再迭代扩展到整个聚类桶。
核心思路
对于聚类桶里的所有字符串,我们可以:
- 从第一个字符串开始作为初始模式
- 依次和桶里的下一个字符串对比,用
SequenceMatcher找到两者的匹配块 - 把不匹配的部分替换成
*,合并连续的*避免冗余 - 用新生成的模式继续和下一个字符串迭代,直到覆盖所有成员
二、Python实现示例(基于difflib)
这里给你一个可以直接和现有聚类代码结合的实现:
import difflib # 你的聚类函数(保留原逻辑并优化了删除索引的方式) def cluster_strings(strings: list[str], threshold: float = 0.9) -> list[list[str]]: clusters = [] while len(strings) > 0: string = strings.pop() clusters.append([string]) indexes_to_remove = [] for idx, string_to_match in enumerate(strings): if difflib.SequenceMatcher(None, string, string_to_match).quick_ratio() > threshold: clusters[-1].append(string_to_match) indexes_to_remove.append(idx) # 反向删除避免索引偏移 for idx in reversed(indexes_to_remove): del strings[idx] return clusters # 生成两两字符串的公共Glob模式 def two_str_to_glob(a: str, b: str) -> str: matcher = difflib.SequenceMatcher(None, a, b, autojunk=False) pattern_parts = [] prev_a, prev_b = 0, 0 for match in matcher.get_matching_blocks(): # 处理匹配块之前的差异部分 if match.a > prev_a or match.b > prev_b: pattern_parts.append("*") # 添加匹配的固定文本 if match.size > 0: pattern_parts.append(a[match.a:match.a+match.size]) prev_a, prev_b = match.a + match.size, match.b + match.size # 处理最后一个匹配块后的剩余差异 if prev_a < len(a) or prev_b < len(b): pattern_parts.append("*") # 合并连续的*,避免冗余 merged_parts = [] for part in pattern_parts: if merged_parts and merged_parts[-1] == "*" and part == "*": continue merged_parts.append(part) return "".join(merged_parts) # 给整个聚类桶生成公共Glob模式 def cluster_to_glob(cluster: list[str]) -> str: if not cluster: return "" current_pattern = cluster[0] for s in cluster[1:]: current_pattern = two_str_to_glob(current_pattern, s) # 提前终止:如果模式已经全是*,没必要继续迭代 if current_pattern == "*": break return current_pattern
使用示例
# 测试你给出的错误示例 sample_errors = [ "Error: [MODULE_FOO] File foo has 5 unsolved dependencies and 4 errors.", "Error: [MODULE_BLA] Files bar and yaz have 123 unsolved dependencies.", "Error: [MODULE_123] File baz has 45 unsolved dependencies and 3 warnings." ] # 先聚类(这里阈值设低一点确保都在一个桶) clusters = cluster_strings(sample_errors.copy(), threshold=0.6) # 给每个聚类生成模式 for idx, cluster in enumerate(clusters): pattern = cluster_to_glob(cluster) print(f"聚类桶 {idx+1}:") print(f"成员: {cluster}") print(f"公共Glob模式: {pattern}\n")
运行后会生成类似你预期的模式,把差异部分替换成*,保留所有公共文本。
三、如果需要更精准的匹配
如果不想用粗粒度的*,而是想区分数字、变量名等特定差异(比如把数字部分替换成[0-9]*),可以在生成模式时加入简单的类型检测:
- 对比差异部分的字符类型(是否全为数字、字母等)
- 用对应的精准通配符替换(比如数字用
[0-9]*,任意字符用*)
四、非Python通用伪代码
如果需要跨语言实现,核心逻辑可以用下面的伪代码:
# 给一组字符串生成公共Glob模式 function generate_common_glob(strings): if strings is empty: return "" current_pattern = strings[0] for each string in strings[1:]: current_pattern = refine_pattern(current_pattern, string) if current_pattern == "*": break return current_pattern # 用新字符串优化现有模式 function refine_pattern(pattern, target_str): # 把模式拆成固定段和* pattern_segments = split_pattern_into_segments(pattern) new_segments = [] current_pos = 0 for segment in pattern_segments: if segment == "*": # 找到target_str中能匹配当前段的最长区间 next_fixed = next segment in pattern_segments that is not * if next_fixed exists: # 在target_str中从current_pos开始找next_fixed的位置 match_pos = find_substring(target_str, next_fixed, start=current_pos) if match_pos != -1: new_segments.append("*") new_segments.append(next_fixed) current_pos = match_pos + len(next_fixed) else: # 固定段必须完全匹配,直接加入 if target_str.find(segment, current_pos) == current_pos: new_segments.append(segment) current_pos += len(segment) else: # 固定段不匹配,替换成* new_segments.append("*") new_segments.append(segment) current_pos = target_str.find(segment) + len(segment) if segment in target_str else len(target_str) # 处理剩余部分 if current_pos < len(target_str): new_segments.append("*") # 合并连续的* merged = [] for seg in new_segments: if merged and merged[-1] == "*" and seg == "*": continue merged.append(seg) return join_segments(merged)
注意事项
- 性能优化:如果聚类桶很大(上百个字符串),可以先找到桶的中心字符串(比如和其他所有字符串相似度最高的那个),只用中心字符串和其他成员生成模式,减少迭代次数。
- Glob转正则:如果需要更严谨的匹配,可以把生成的Glob模式转换成正则表达式(比如把
*换成.*,转义[]、()等特殊字符),这样能支持复杂场景的匹配。 - 阈值调整:如果生成的模式太宽泛(全是
*),可以提高聚类的相似度阈值,让每个桶内的字符串更相似,生成的模式更有意义。
内容来源于stack exchange
相关产品推荐
相关产品推荐

