解析 log 文件,提取节点树结构 Returns: nodes: {node_id: NodeInfo} root_id: 根节点ID
(log_file: str)
| 31 | |
| 32 | |
| 33 | def parse_log(log_file: str) -> Tuple[Dict[str, NodeInfo], str]: |
| 34 | """ |
| 35 | 解析 log 文件,提取节点树结构 |
| 36 | |
| 37 | Returns: |
| 38 | nodes: {node_id: NodeInfo} |
| 39 | root_id: 根节点ID |
| 40 | """ |
| 41 | nodes: Dict[str, NodeInfo] = {} |
| 42 | root_id = None |
| 43 | |
| 44 | print(f"Parsing {log_file}...") |
| 45 | |
| 46 | with open(log_file, 'r', encoding='utf-8') as f: |
| 47 | for line in f: |
| 48 | # 提取根节点 - 支持两种格式 |
| 49 | # 新格式:[SELECT] Examining X, metric=Y (stage=root, ...) |
| 50 | # 旧格式:[SELECT] Iter 1: examining node X (stage=root, ...) |
| 51 | if 'stage=root' in line and ('[SELECT] Examining' in line or 'examining node' in line): |
| 52 | match = re.search(r'Examining\s+(\w+)|examining node\s+(\w+)', line) |
| 53 | if match: |
| 54 | root_id = match.group(1) or match.group(2) |
| 55 | if root_id not in nodes: |
| 56 | nodes[root_id] = NodeInfo(root_id, "root") |
| 57 | |
| 58 | # 提取节点创建:draft |
| 59 | if '[draft] node=' in line and 'iter=1/' in line: |
| 60 | match = re.search(r'node=(\w+).*branch=(\d+)', line) |
| 61 | if match: |
| 62 | node_id = match.group(1) |
| 63 | if node_id not in nodes: |
| 64 | nodes[node_id] = NodeInfo(node_id, "draft", root_id) |
| 65 | if root_id and root_id in nodes: |
| 66 | nodes[root_id].children_ids.append(node_id) |
| 67 | |
| 68 | # 提取节点创建:improve |
| 69 | if '[improve] parent=' in line and 'iter=1/' in line: |
| 70 | match = re.search(r'parent=(\w+).*node=(\w+)', line) |
| 71 | if match: |
| 72 | parent_id = match.group(1) |
| 73 | node_id = match.group(2) |
| 74 | if node_id not in nodes: |
| 75 | nodes[node_id] = NodeInfo(node_id, "improve", parent_id) |
| 76 | if parent_id in nodes: |
| 77 | nodes[parent_id].children_ids.append(node_id) |
| 78 | |
| 79 | # 提取 run 状态 |
| 80 | if 'node=' in line and ' run ' in line: |
| 81 | match = re.search(r'node=(\w+).*run (OK|FAIL)', line) |
| 82 | if match: |
| 83 | node_id = match.group(1) |
| 84 | status = match.group(2) |
| 85 | if node_id in nodes: |
| 86 | nodes[node_id].run_status = status |
| 87 | |
| 88 | # 提取 metric - 支持多种格式 |
| 89 | # 注意:使用独立的if而不是elif,确保所有格式都能被检查 |
| 90 | # 优先级:is the best > Comparing > Examining |
no test coverage detected