戒酒的李白

The forum recording function has been initially completed.

  1 +"""
  2 +ForumEgine - 监控和记录三个Engine的SummaryNode和ReportFormattingNode输出
  3 +"""
  4 +
  5 +from .monitor import LogMonitor
  6 +
  7 +__all__ = ['LogMonitor']
  1 +"""
  2 +日志监控器 - 实时监控三个log文件中的SummaryNode和ReportFormattingNode输出
  3 +"""
  4 +
  5 +import os
  6 +import time
  7 +import threading
  8 +from pathlib import Path
  9 +from datetime import datetime
  10 +import re
  11 +from typing import Dict, Optional, List
  12 +from threading import Lock
  13 +
  14 +class LogMonitor:
  15 + """基于文件变化的智能日志监控器"""
  16 +
  17 + def __init__(self, log_dir: str = "logs"):
  18 + """初始化日志监控器"""
  19 + self.log_dir = Path(log_dir)
  20 + self.forum_log_file = self.log_dir / "forum.log"
  21 +
  22 + # 要监控的日志文件
  23 + self.monitored_logs = {
  24 + 'insight': self.log_dir / 'insight.log',
  25 + 'media': self.log_dir / 'media.log',
  26 + 'query': self.log_dir / 'query.log'
  27 + }
  28 +
  29 + # 监控状态
  30 + self.is_monitoring = False
  31 + self.monitor_thread = None
  32 + self.file_positions = {} # 记录每个文件的读取位置
  33 + self.file_line_counts = {} # 记录每个文件的行数
  34 + self.is_searching = False # 是否正在搜索
  35 + self.search_inactive_count = 0 # 搜索非活跃计数器
  36 + self.write_lock = Lock() # 写入锁,防止并发写入冲突
  37 +
  38 + # 目标节点名称 - 直接匹配字符串
  39 + self.target_nodes = [
  40 + 'FirstSummaryNode',
  41 + 'ReflectionSummaryNode',
  42 + 'ReportFormattingNode'
  43 + ]
  44 +
  45 + # 确保logs目录存在
  46 + self.log_dir.mkdir(exist_ok=True)
  47 +
  48 + def clear_forum_log(self):
  49 + """清空forum.log文件"""
  50 + try:
  51 + if self.forum_log_file.exists():
  52 + self.forum_log_file.unlink()
  53 +
  54 + # 创建新的forum.log文件并写入开始标记
  55 + with open(self.forum_log_file, 'w', encoding='utf-8') as f:
  56 + start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  57 + f.write(f"=== ForumEgine 监控开始 - {start_time} ===\n")
  58 +
  59 + print(f"ForumEgine: forum.log 已清空并初始化")
  60 +
  61 + except Exception as e:
  62 + print(f"ForumEgine: 清空forum.log失败: {e}")
  63 +
  64 + def write_to_forum_log(self, content: str):
  65 + """写入内容到forum.log(线程安全)"""
  66 + try:
  67 + with self.write_lock: # 使用锁确保线程安全
  68 + with open(self.forum_log_file, 'a', encoding='utf-8') as f:
  69 + timestamp = datetime.now().strftime('%H:%M:%S')
  70 + f.write(f"[{timestamp}] {content}\n")
  71 + f.flush()
  72 + except Exception as e:
  73 + print(f"ForumEgine: 写入forum.log失败: {e}")
  74 +
  75 + def is_target_log_line(self, line: str) -> bool:
  76 + """检查是否是目标日志行(SummaryNode或ReportFormattingNode)"""
  77 + # 简单字符串包含检查,更可靠
  78 + for node_name in self.target_nodes:
  79 + if node_name in line:
  80 + return True
  81 + return False
  82 +
  83 + def extract_node_content(self, line: str) -> Optional[str]:
  84 + """提取节点内容"""
  85 + # 移除时间戳部分,保留节点名称和消息
  86 + # 格式: [HH:MM:SS] [NodeName] message
  87 + match = re.search(r'\[\d{2}:\d{2}:\d{2}\]\s*(.+)', line)
  88 + if match:
  89 + return match.group(1).strip()
  90 + return line.strip()
  91 +
  92 + def get_file_size(self, file_path: Path) -> int:
  93 + """获取文件大小"""
  94 + try:
  95 + return file_path.stat().st_size if file_path.exists() else 0
  96 + except:
  97 + return 0
  98 +
  99 + def get_file_line_count(self, file_path: Path) -> int:
  100 + """获取文件行数"""
  101 + try:
  102 + if not file_path.exists():
  103 + return 0
  104 + with open(file_path, 'r', encoding='utf-8') as f:
  105 + return sum(1 for _ in f)
  106 + except:
  107 + return 0
  108 +
  109 + # 移除这个方法,逻辑已经合并到monitor_logs中
  110 +
  111 + def read_new_lines(self, file_path: Path, app_name: str) -> List[str]:
  112 + """读取文件中的新行"""
  113 + new_lines = []
  114 +
  115 + try:
  116 + if not file_path.exists():
  117 + return new_lines
  118 +
  119 + current_size = self.get_file_size(file_path)
  120 + last_position = self.file_positions.get(app_name, 0)
  121 +
  122 + # 如果文件变小了,说明被清空了,重新从头开始
  123 + if current_size < last_position:
  124 + last_position = 0
  125 +
  126 + if current_size > last_position:
  127 + with open(file_path, 'r', encoding='utf-8') as f:
  128 + f.seek(last_position)
  129 + new_content = f.read()
  130 + new_lines = new_content.split('\n')
  131 +
  132 + # 更新位置
  133 + self.file_positions[app_name] = f.tell()
  134 +
  135 + # 过滤空行
  136 + new_lines = [line.strip() for line in new_lines if line.strip()]
  137 +
  138 + except Exception as e:
  139 + print(f"ForumEgine: 读取{app_name}日志失败: {e}")
  140 +
  141 + return new_lines
  142 +
  143 + def monitor_logs(self):
  144 + """智能监控日志文件"""
  145 + print("ForumEgine: 开始智能监控日志文件...")
  146 +
  147 + # 初始化文件行数和位置 - 记录当前状态作为基线
  148 + for app_name, log_file in self.monitored_logs.items():
  149 + self.file_line_counts[app_name] = self.get_file_line_count(log_file)
  150 + self.file_positions[app_name] = self.get_file_size(log_file)
  151 + print(f"ForumEgine: {app_name} 基线行数: {self.file_line_counts[app_name]}")
  152 +
  153 + while self.is_monitoring:
  154 + try:
  155 + # 同时检测三个log文件的变化
  156 + any_growth = False
  157 + any_shrink = False
  158 + captured_any = False
  159 +
  160 + # 为每个log文件独立处理
  161 + for app_name, log_file in self.monitored_logs.items():
  162 + current_lines = self.get_file_line_count(log_file)
  163 + previous_lines = self.file_line_counts.get(app_name, 0)
  164 +
  165 + if current_lines > previous_lines:
  166 + any_growth = True
  167 + # 立即读取新增内容
  168 + new_lines = self.read_new_lines(log_file, app_name)
  169 +
  170 + # 先检查是否需要触发搜索(只触发一次)
  171 + if not self.is_searching:
  172 + for line in new_lines:
  173 + if line.strip() and 'FirstSummaryNode' in line:
  174 + print(f"ForumEgine: 在{app_name}中检测到FirstSummaryNode,开始监控记录")
  175 + self.is_searching = True
  176 + self.search_inactive_count = 0
  177 + # 清空forum.log开始新会话
  178 + self.clear_forum_log()
  179 + break # 找到一个就够了,跳出循环
  180 +
  181 + # 处理所有新增内容(如果正在搜索状态)
  182 + if self.is_searching:
  183 + for line in new_lines:
  184 + if line.strip() and self.is_target_log_line(line):
  185 + # 立即记录目标节点输出
  186 + formatted_content = f"[{app_name.upper()}] {line.strip()}"
  187 + self.write_to_forum_log(formatted_content)
  188 + print(f"ForumEgine: 捕获 - {formatted_content}")
  189 + captured_any = True
  190 +
  191 + elif current_lines < previous_lines:
  192 + any_shrink = True
  193 + print(f"ForumEgine: 检测到 {app_name} 日志缩短,将重置基线")
  194 + # 重置文件位置到新的文件末尾
  195 + self.file_positions[app_name] = self.get_file_size(log_file)
  196 +
  197 + # 更新行数记录
  198 + self.file_line_counts[app_name] = current_lines
  199 +
  200 + # 检查是否应该结束当前搜索会话
  201 + if self.is_searching:
  202 + if any_shrink:
  203 + # log变短,结束当前搜索会话,重置为等待状态
  204 + print("ForumEgine: 日志缩短,结束当前搜索会话,回到等待状态")
  205 + self.is_searching = False
  206 + self.search_inactive_count = 0
  207 + # 写入结束标记
  208 + end_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  209 + self.write_to_forum_log(f"=== ForumEgine 搜索会话结束 - {end_time} ===")
  210 + print("ForumEgine: 已重置基线,等待下次FirstSummaryNode触发")
  211 + elif not any_growth and not captured_any:
  212 + # 没有增长也没有捕获内容,增加非活跃计数
  213 + self.search_inactive_count += 1
  214 + if self.search_inactive_count >= 30: # 30秒无活动才结束
  215 + print("ForumEgine: 长时间无活动,结束搜索会话")
  216 + self.is_searching = False
  217 + self.search_inactive_count = 0
  218 + # 写入结束标记
  219 + end_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  220 + self.write_to_forum_log(f"=== ForumEgine 搜索会话超时结束 - {end_time} ===")
  221 + else:
  222 + self.search_inactive_count = 0 # 重置计数器
  223 +
  224 + # 短暂休眠
  225 + time.sleep(1)
  226 +
  227 + except Exception as e:
  228 + print(f"ForumEgine: 监控过程中出错: {e}")
  229 + import traceback
  230 + traceback.print_exc()
  231 + time.sleep(2)
  232 +
  233 + print("ForumEgine: 停止监控日志文件")
  234 +
  235 + def start_monitoring(self):
  236 + """开始智能监控"""
  237 + if self.is_monitoring:
  238 + print("ForumEgine: 监控已经在运行中")
  239 + return False
  240 +
  241 + try:
  242 + # 启动监控
  243 + self.is_monitoring = True
  244 + self.monitor_thread = threading.Thread(target=self.monitor_logs, daemon=True)
  245 + self.monitor_thread.start()
  246 +
  247 + print("ForumEgine: 智能监控已启动")
  248 + return True
  249 +
  250 + except Exception as e:
  251 + print(f"ForumEgine: 启动监控失败: {e}")
  252 + self.is_monitoring = False
  253 + return False
  254 +
  255 + def stop_monitoring(self):
  256 + """停止监控"""
  257 + if not self.is_monitoring:
  258 + print("ForumEgine: 监控未运行")
  259 + return
  260 +
  261 + try:
  262 + self.is_monitoring = False
  263 +
  264 + if self.monitor_thread and self.monitor_thread.is_alive():
  265 + self.monitor_thread.join(timeout=2)
  266 +
  267 + # 写入结束标记
  268 + end_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  269 + self.write_to_forum_log(f"=== ForumEgine 监控结束 - {end_time} ===")
  270 +
  271 + print("ForumEgine: 监控已停止")
  272 +
  273 + except Exception as e:
  274 + print(f"ForumEgine: 停止监控失败: {e}")
  275 +
  276 + def get_forum_log_content(self) -> List[str]:
  277 + """获取forum.log的内容"""
  278 + try:
  279 + if not self.forum_log_file.exists():
  280 + return []
  281 +
  282 + with open(self.forum_log_file, 'r', encoding='utf-8') as f:
  283 + return [line.rstrip('\n\r') for line in f.readlines()]
  284 +
  285 + except Exception as e:
  286 + print(f"ForumEgine: 读取forum.log失败: {e}")
  287 + return []
  288 +
  289 +
  290 +# 全局监控器实例
  291 +_monitor_instance = None
  292 +
  293 +def get_monitor() -> LogMonitor:
  294 + """获取全局监控器实例"""
  295 + global _monitor_instance
  296 + if _monitor_instance is None:
  297 + _monitor_instance = LogMonitor()
  298 + return _monitor_instance
  299 +
  300 +def start_forum_monitoring():
  301 + """启动ForumEgine智能监控"""
  302 + return get_monitor().start_monitoring()
  303 +
  304 +def stop_forum_monitoring():
  305 + """停止ForumEgine监控"""
  306 + get_monitor().stop_monitoring()
  307 +
  308 +def get_forum_log():
  309 + """获取forum.log内容"""
  310 + return get_monitor().get_forum_log_content()
@@ -79,7 +79,7 @@ class ReportStructureNode(StateMutationNode): @@ -79,7 +79,7 @@ class ReportStructureNode(StateMutationNode):
79 cleaned_output = clean_json_tags(cleaned_output) 79 cleaned_output = clean_json_tags(cleaned_output)
80 80
81 # 记录清理后的输出用于调试 81 # 记录清理后的输出用于调试
82 - self.log_info(f"清理后的输出: {cleaned_output[:200]}...") 82 + self.log_info(f"清理后的输出: {cleaned_output}")
83 83
84 # 解析JSON 84 # 解析JSON
85 try: 85 try:
@@ -93,7 +93,7 @@ class FirstSearchNode(BaseNode): @@ -93,7 +93,7 @@ class FirstSearchNode(BaseNode):
93 cleaned_output = clean_json_tags(cleaned_output) 93 cleaned_output = clean_json_tags(cleaned_output)
94 94
95 # 记录清理后的输出用于调试 95 # 记录清理后的输出用于调试
96 - self.log_info(f"清理后的输出: {cleaned_output[:200]}...") 96 + self.log_info(f"清理后的输出: {cleaned_output}")
97 97
98 # 解析JSON 98 # 解析JSON
99 try: 99 try:
@@ -228,7 +228,7 @@ class ReflectionNode(BaseNode): @@ -228,7 +228,7 @@ class ReflectionNode(BaseNode):
228 cleaned_output = clean_json_tags(cleaned_output) 228 cleaned_output = clean_json_tags(cleaned_output)
229 229
230 # 记录清理后的输出用于调试 230 # 记录清理后的输出用于调试
231 - self.log_info(f"清理后的输出: {cleaned_output[:200]}...") 231 + self.log_info(f"清理后的输出: {cleaned_output}")
232 232
233 # 解析JSON 233 # 解析JSON
234 try: 234 try:
@@ -97,7 +97,7 @@ class FirstSummaryNode(StateMutationNode): @@ -97,7 +97,7 @@ class FirstSummaryNode(StateMutationNode):
97 cleaned_output = clean_json_tags(cleaned_output) 97 cleaned_output = clean_json_tags(cleaned_output)
98 98
99 # 记录清理后的输出用于调试 99 # 记录清理后的输出用于调试
100 - self.log_info(f"清理后的输出: {cleaned_output[:200]}...") 100 + self.log_info(f"清理后的输出: {cleaned_output}")
101 101
102 # 解析JSON 102 # 解析JSON
103 try: 103 try:
@@ -243,7 +243,7 @@ class ReflectionSummaryNode(StateMutationNode): @@ -243,7 +243,7 @@ class ReflectionSummaryNode(StateMutationNode):
243 cleaned_output = clean_json_tags(cleaned_output) 243 cleaned_output = clean_json_tags(cleaned_output)
244 244
245 # 记录清理后的输出用于调试 245 # 记录清理后的输出用于调试
246 - self.log_info(f"清理后的输出: {cleaned_output[:200]}...") 246 + self.log_info(f"清理后的输出: {cleaned_output}")
247 247
248 # 解析JSON 248 # 解析JSON
249 try: 249 try:
@@ -79,7 +79,7 @@ class ReportStructureNode(StateMutationNode): @@ -79,7 +79,7 @@ class ReportStructureNode(StateMutationNode):
79 cleaned_output = clean_json_tags(cleaned_output) 79 cleaned_output = clean_json_tags(cleaned_output)
80 80
81 # 记录清理后的输出用于调试 81 # 记录清理后的输出用于调试
82 - self.log_info(f"清理后的输出: {cleaned_output[:200]}...") 82 + self.log_info(f"清理后的输出: {cleaned_output}")
83 83
84 # 解析JSON 84 # 解析JSON
85 try: 85 try:
@@ -93,7 +93,7 @@ class FirstSearchNode(BaseNode): @@ -93,7 +93,7 @@ class FirstSearchNode(BaseNode):
93 cleaned_output = clean_json_tags(cleaned_output) 93 cleaned_output = clean_json_tags(cleaned_output)
94 94
95 # 记录清理后的输出用于调试 95 # 记录清理后的输出用于调试
96 - self.log_info(f"清理后的输出: {cleaned_output[:200]}...") 96 + self.log_info(f"清理后的输出: {cleaned_output}")
97 97
98 # 解析JSON 98 # 解析JSON
99 try: 99 try:
@@ -228,7 +228,7 @@ class ReflectionNode(BaseNode): @@ -228,7 +228,7 @@ class ReflectionNode(BaseNode):
228 cleaned_output = clean_json_tags(cleaned_output) 228 cleaned_output = clean_json_tags(cleaned_output)
229 229
230 # 记录清理后的输出用于调试 230 # 记录清理后的输出用于调试
231 - self.log_info(f"清理后的输出: {cleaned_output[:200]}...") 231 + self.log_info(f"清理后的输出: {cleaned_output}")
232 232
233 # 解析JSON 233 # 解析JSON
234 try: 234 try:
@@ -97,7 +97,7 @@ class FirstSummaryNode(StateMutationNode): @@ -97,7 +97,7 @@ class FirstSummaryNode(StateMutationNode):
97 cleaned_output = clean_json_tags(cleaned_output) 97 cleaned_output = clean_json_tags(cleaned_output)
98 98
99 # 记录清理后的输出用于调试 99 # 记录清理后的输出用于调试
100 - self.log_info(f"清理后的输出: {cleaned_output[:200]}...") 100 + self.log_info(f"清理后的输出: {cleaned_output}")
101 101
102 # 解析JSON 102 # 解析JSON
103 try: 103 try:
@@ -243,7 +243,7 @@ class ReflectionSummaryNode(StateMutationNode): @@ -243,7 +243,7 @@ class ReflectionSummaryNode(StateMutationNode):
243 cleaned_output = clean_json_tags(cleaned_output) 243 cleaned_output = clean_json_tags(cleaned_output)
244 244
245 # 记录清理后的输出用于调试 245 # 记录清理后的输出用于调试
246 - self.log_info(f"清理后的输出: {cleaned_output[:200]}...") 246 + self.log_info(f"清理后的输出: {cleaned_output}")
247 247
248 # 解析JSON 248 # 解析JSON
249 try: 249 try:
@@ -79,7 +79,7 @@ class ReportStructureNode(StateMutationNode): @@ -79,7 +79,7 @@ class ReportStructureNode(StateMutationNode):
79 cleaned_output = clean_json_tags(cleaned_output) 79 cleaned_output = clean_json_tags(cleaned_output)
80 80
81 # 记录清理后的输出用于调试 81 # 记录清理后的输出用于调试
82 - self.log_info(f"清理后的输出: {cleaned_output[:200]}...") 82 + self.log_info(f"清理后的输出: {cleaned_output}")
83 83
84 # 解析JSON 84 # 解析JSON
85 try: 85 try:
@@ -93,7 +93,7 @@ class FirstSearchNode(BaseNode): @@ -93,7 +93,7 @@ class FirstSearchNode(BaseNode):
93 cleaned_output = clean_json_tags(cleaned_output) 93 cleaned_output = clean_json_tags(cleaned_output)
94 94
95 # 记录清理后的输出用于调试 95 # 记录清理后的输出用于调试
96 - self.log_info(f"清理后的输出: {cleaned_output[:200]}...") 96 + self.log_info(f"清理后的输出: {cleaned_output}")
97 97
98 # 解析JSON 98 # 解析JSON
99 try: 99 try:
@@ -228,7 +228,7 @@ class ReflectionNode(BaseNode): @@ -228,7 +228,7 @@ class ReflectionNode(BaseNode):
228 cleaned_output = clean_json_tags(cleaned_output) 228 cleaned_output = clean_json_tags(cleaned_output)
229 229
230 # 记录清理后的输出用于调试 230 # 记录清理后的输出用于调试
231 - self.log_info(f"清理后的输出: {cleaned_output[:200]}...") 231 + self.log_info(f"清理后的输出: {cleaned_output}")
232 232
233 # 解析JSON 233 # 解析JSON
234 try: 234 try:
@@ -97,7 +97,7 @@ class FirstSummaryNode(StateMutationNode): @@ -97,7 +97,7 @@ class FirstSummaryNode(StateMutationNode):
97 cleaned_output = clean_json_tags(cleaned_output) 97 cleaned_output = clean_json_tags(cleaned_output)
98 98
99 # 记录清理后的输出用于调试 99 # 记录清理后的输出用于调试
100 - self.log_info(f"清理后的输出: {cleaned_output[:200]}...") 100 + self.log_info(f"清理后的输出: {cleaned_output}")
101 101
102 # 解析JSON 102 # 解析JSON
103 try: 103 try:
@@ -243,7 +243,7 @@ class ReflectionSummaryNode(StateMutationNode): @@ -243,7 +243,7 @@ class ReflectionSummaryNode(StateMutationNode):
243 cleaned_output = clean_json_tags(cleaned_output) 243 cleaned_output = clean_json_tags(cleaned_output)
244 244
245 # 记录清理后的输出用于调试 245 # 记录清理后的输出用于调试
246 - self.log_info(f"清理后的输出: {cleaned_output[:200]}...") 246 + self.log_info(f"清理后的输出: {cleaned_output}")
247 247
248 # 解析JSON 248 # 解析JSON
249 try: 249 try:
@@ -30,6 +30,39 @@ os.environ['PYTHONUTF8'] = '1' @@ -30,6 +30,39 @@ os.environ['PYTHONUTF8'] = '1'
30 LOG_DIR = Path('logs') 30 LOG_DIR = Path('logs')
31 LOG_DIR.mkdir(exist_ok=True) 31 LOG_DIR.mkdir(exist_ok=True)
32 32
  33 +# 初始化ForumEgine的forum.log文件
  34 +def init_forum_log():
  35 + """初始化forum.log文件"""
  36 + try:
  37 + forum_log_file = LOG_DIR / "forum.log"
  38 + if not forum_log_file.exists():
  39 + with open(forum_log_file, 'w', encoding='utf-8') as f:
  40 + start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  41 + f.write(f"=== ForumEgine 系统初始化 - {start_time} ===\n")
  42 + print(f"ForumEgine: forum.log 已初始化")
  43 + except Exception as e:
  44 + print(f"ForumEgine: 初始化forum.log失败: {e}")
  45 +
  46 +# 初始化forum.log
  47 +init_forum_log()
  48 +
  49 +# 启动ForumEgine智能监控
  50 +def start_forum_engine():
  51 + """启动ForumEgine智能监控"""
  52 + try:
  53 + from ForumEgine.monitor import start_forum_monitoring
  54 + print("ForumEgine: 启动智能监控...")
  55 + success = start_forum_monitoring()
  56 + if success:
  57 + print("ForumEgine: 智能监控已启动,将自动检测搜索活动")
  58 + else:
  59 + print("ForumEgine: 智能监控启动失败")
  60 + except Exception as e:
  61 + print(f"ForumEgine: 启动智能监控失败: {e}")
  62 +
  63 +# 启动ForumEgine
  64 +start_forum_engine()
  65 +
33 # 全局变量存储进程信息 66 # 全局变量存储进程信息
34 processes = { 67 processes = {
35 'insight': {'process': None, 'port': 8501, 'status': 'stopped', 'output': [], 'log_file': None}, 68 'insight': {'process': None, 'port': 8501, 'status': 'stopped', 'output': [], 'log_file': None},
@@ -382,6 +415,43 @@ def test_log(app_name): @@ -382,6 +415,43 @@ def test_log(app_name):
382 'message': f'测试消息已写入 {app_name} 日志' 415 'message': f'测试消息已写入 {app_name} 日志'
383 }) 416 })
384 417
  418 +@app.route('/api/forum/start')
  419 +def start_forum_monitoring_api():
  420 + """手动启动ForumEgine监控"""
  421 + try:
  422 + from ForumEgine.monitor import start_forum_monitoring
  423 + success = start_forum_monitoring()
  424 + if success:
  425 + return jsonify({'success': True, 'message': 'ForumEgine监控已启动'})
  426 + else:
  427 + return jsonify({'success': False, 'message': 'ForumEgine监控启动失败'})
  428 + except Exception as e:
  429 + return jsonify({'success': False, 'message': f'启动监控失败: {str(e)}'})
  430 +
  431 +@app.route('/api/forum/stop')
  432 +def stop_forum_monitoring_api():
  433 + """手动停止ForumEgine监控"""
  434 + try:
  435 + from ForumEgine.monitor import stop_forum_monitoring
  436 + stop_forum_monitoring()
  437 + return jsonify({'success': True, 'message': 'ForumEgine监控已停止'})
  438 + except Exception as e:
  439 + return jsonify({'success': False, 'message': f'停止监控失败: {str(e)}'})
  440 +
  441 +@app.route('/api/forum/log')
  442 +def get_forum_log():
  443 + """获取ForumEgine的forum.log内容"""
  444 + try:
  445 + from ForumEgine.monitor import get_forum_log
  446 + log_content = get_forum_log()
  447 + return jsonify({
  448 + 'success': True,
  449 + 'log_lines': log_content,
  450 + 'total_lines': len(log_content)
  451 + })
  452 + except Exception as e:
  453 + return jsonify({'success': False, 'message': f'读取forum.log失败: {str(e)}'})
  454 +
385 @app.route('/api/search', methods=['POST']) 455 @app.route('/api/search', methods=['POST'])
386 def search(): 456 def search():
387 """统一搜索接口""" 457 """统一搜索接口"""
@@ -391,6 +461,9 @@ def search(): @@ -391,6 +461,9 @@ def search():
391 if not query: 461 if not query:
392 return jsonify({'success': False, 'message': '搜索查询不能为空'}) 462 return jsonify({'success': False, 'message': '搜索查询不能为空'})
393 463
  464 + # ForumEgine智能监控已经在后台运行,会自动检测搜索活动
  465 + print("ForumEgine: 搜索请求已收到,智能监控将自动检测日志变化")
  466 +
394 # 检查哪些应用正在运行 467 # 检查哪些应用正在运行
395 check_app_status() 468 check_app_status()
396 running_apps = [name for name, info in processes.items() if info['status'] == 'running'] 469 running_apps = [name for name, info in processes.items() if info['status'] == 'running']
@@ -418,6 +491,9 @@ def search(): @@ -418,6 +491,9 @@ def search():
418 except Exception as e: 491 except Exception as e:
419 results[app_name] = {'success': False, 'message': str(e)} 492 results[app_name] = {'success': False, 'message': str(e)}
420 493
  494 + # 搜索完成后可以选择停止监控,或者让它继续运行以捕获后续的处理日志
  495 + # 这里我们让监控继续运行,用户可以通过其他接口手动停止
  496 +
421 return jsonify({ 497 return jsonify({
422 'success': True, 498 'success': True,
423 'query': query, 499 'query': query,