6969}
7070
7171
72+ def _exit_code_for_alerts (alerts , exit_on : str ) -> int :
73+ threshold = _EXIT_THRESHOLDS .get (exit_on )
74+ if threshold is None :
75+ return 0
76+ return 1 if any (a .level .score >= threshold for a in alerts ) else 0
77+
78+
7279def _dispatch_subcommand (argv : List [str ]) -> bool :
7380 if len (argv ) < 2 or argv [1 ] not in {"validate-rules" , "benchmark" , "explain" , "ssh" }:
7481 return False
@@ -153,6 +160,7 @@ def _cmd_benchmark(argv: List[str]) -> None:
153160 parser .add_argument ("--size-mb" , type = int , default = 10 , help = "合成日志大小,默认 10MB" )
154161 parser .add_argument ("--profile" , choices = ("default" , "cn-hvv" ), default = "cn-hvv" )
155162 parser .add_argument ("-j" , "--jobs" , type = int , default = 0 )
163+ parser .add_argument ("--memory" , action = "store_true" , help = "启用 tracemalloc 统计峰值内存;会让耗时基准偏慢" )
156164 args = parser .parse_args (argv )
157165
158166 files = _collect_files (args .paths )
@@ -162,14 +170,21 @@ def _cmd_benchmark(argv: List[str]) -> None:
162170 files = [str (temp_path )]
163171
164172 reset_counter ()
165- tracemalloc .start ()
173+ if args .memory :
174+ tracemalloc .start ()
166175 started = time .perf_counter ()
176+ parse_started = time .perf_counter ()
167177 parse_results = _parse_files (files , args .jobs , quiet = True )
178+ parse_elapsed = time .perf_counter () - parse_started
168179 all_events = [event for result in parse_results for event in result .events ]
180+ detect_started = time .perf_counter ()
169181 summary = run_detection (all_events , profile = args .profile )
182+ detect_elapsed = time .perf_counter () - detect_started
170183 elapsed = time .perf_counter () - started
171- current , peak = tracemalloc .get_traced_memory ()
172- tracemalloc .stop ()
184+ peak = None
185+ if args .memory :
186+ _current , peak = tracemalloc .get_traced_memory ()
187+ tracemalloc .stop ()
173188
174189 total_bytes = sum (result .file_size_bytes for result in parse_results )
175190 mb = total_bytes / (1024 * 1024 ) if total_bytes else 0
@@ -181,9 +196,14 @@ def _cmd_benchmark(argv: List[str]) -> None:
181196 print (f" alerts: { len (summary .alerts )} " )
182197 print (f" incidents: { len (summary .incidents )} " )
183198 print (f" elapsed: { elapsed :.3f} s" )
199+ print (f" parse: { parse_elapsed :.3f} s" )
200+ print (f" enrich+detect: { detect_elapsed :.3f} s" )
184201 print (f" throughput: { mb / elapsed if elapsed > 0 else 0 :.2f} MB/s" )
185202 print (f" event rate: { eps :.0f} events/s" )
186- print (f" peak memory: { peak / (1024 * 1024 ):.2f} MB" )
203+ if peak is not None :
204+ print (f" peak memory: { peak / (1024 * 1024 ):.2f} MB" )
205+ else :
206+ print (" peak memory: not measured (use --memory)" )
187207 if temp_path :
188208 try :
189209 temp_path .unlink ()
@@ -556,6 +576,12 @@ def main():
556576 summary = run_result .summary
557577 if run_result .suppressed_events :
558578 print (f"\n ✓ 白名单过滤完成,压制 { run_result .suppressed_events } 条事件" )
579+ if run_result .parse_errors :
580+ print (f"\n ⚠️ 有 { len (run_result .parse_errors )} 个输入解析失败,已从本次分析结果中排除:" , file = sys .stderr )
581+ for item in run_result .parse_errors [:10 ]:
582+ print (f" - { item } " , file = sys .stderr )
583+ if len (run_result .parse_errors ) > 10 :
584+ print (f" ... 还有 { len (run_result .parse_errors ) - 10 } 个失败输入未展示" , file = sys .stderr )
559585
560586 print (f"\n ✓ 解析完成,共 { summary .total_events } 条事件\n " )
561587
@@ -571,26 +597,25 @@ def main():
571597 args .full_evidence ,
572598 )
573599
574- write_reports (
575- parse_results ,
576- summary ,
577- AnalysisOutputs (
578- html = args .html ,
579- json = args .json ,
580- csv = args .csv ,
581- ioc = args .ioc ,
582- sarif = args .sarif ,
583- bundle_dir = args .out ,
584- ),
585- )
600+ try :
601+ write_reports (
602+ parse_results ,
603+ summary ,
604+ AnalysisOutputs (
605+ html = args .html ,
606+ json = args .json ,
607+ csv = args .csv ,
608+ ioc = args .ioc ,
609+ sarif = args .sarif ,
610+ bundle_dir = args .out ,
611+ ),
612+ )
613+ except OSError as e :
614+ print (f"\n ❌ 报告写入失败: { e } " , file = sys .stderr )
615+ sys .exit (1 )
586616
587617 # 退出码:可通过 --exit-on 控制触发等级
588- threshold = _EXIT_THRESHOLDS .get (args .exit_on )
589- if threshold is None :
590- sys .exit (0 )
591- if any (a .level .score >= threshold for a in summary .alerts ):
592- sys .exit (1 )
593- sys .exit (0 )
618+ sys .exit (_exit_code_for_alerts (summary .alerts , args .exit_on ))
594619
595620
596621if __name__ == "__main__" :
0 commit comments