Update Report Engine Log Display Method
This commit is contained in:
+43
-3
@@ -233,14 +233,54 @@ class ReportAgent:
|
||||
- 确保日志目录存在;
|
||||
- 使用独立的 loguru sink 写入 Report Engine 专属 log 文件,
|
||||
避免与其他子系统混淆。
|
||||
- 【修复】配置实时日志写入,禁用缓冲,确保前端实时看到日志
|
||||
- 【修复】防止重复添加handler
|
||||
"""
|
||||
# 确保日志目录存在
|
||||
log_dir = os.path.dirname(self.config.LOG_FILE)
|
||||
os.makedirs(log_dir, exist_ok=True)
|
||||
|
||||
# 创建专用的logger,避免与其他模块冲突
|
||||
# 修改日志级别为DEBUG,确保DEBUG、INFO、WARNING、ERROR级别的日志都能被记录
|
||||
logger.add(self.config.LOG_FILE, level="DEBUG")
|
||||
# 【修复】检查是否已经添加过这个文件的handler,避免重复
|
||||
# loguru会自动去重,但显式检查更安全
|
||||
log_file_path = str(Path(self.config.LOG_FILE).resolve())
|
||||
|
||||
# 检查现有的handlers
|
||||
handler_exists = False
|
||||
for handler_id, handler_config in logger._core.handlers.items():
|
||||
if hasattr(handler_config, 'sink'):
|
||||
sink = handler_config.sink
|
||||
# 检查是否是文件sink且路径相同
|
||||
if hasattr(sink, '_name') and sink._name == log_file_path:
|
||||
handler_exists = True
|
||||
logger.debug(f"日志handler已存在,跳过添加: {log_file_path}")
|
||||
break
|
||||
|
||||
if not handler_exists:
|
||||
# 【修复】创建专用的logger,配置实时写入
|
||||
# - enqueue=False: 禁用异步队列,立即写入
|
||||
# - buffering=1: 行缓冲,每条日志立即刷新到文件
|
||||
# - level="DEBUG": 记录所有级别的日志
|
||||
# - encoding="utf-8": 明确指定UTF-8编码
|
||||
# - mode="a": 追加模式,保留历史日志
|
||||
handler_id = logger.add(
|
||||
self.config.LOG_FILE,
|
||||
level="DEBUG",
|
||||
enqueue=False, # 禁用异步队列,同步写入
|
||||
buffering=1, # 行缓冲,每行立即写入
|
||||
serialize=False, # 普通文本格式,不序列化为JSON
|
||||
encoding="utf-8", # 明确UTF-8编码
|
||||
mode="a" # 追加模式
|
||||
)
|
||||
logger.debug(f"已添加日志handler (ID: {handler_id}): {self.config.LOG_FILE}")
|
||||
|
||||
# 【修复】验证日志文件可写
|
||||
try:
|
||||
with open(self.config.LOG_FILE, 'a', encoding='utf-8') as f:
|
||||
f.write('') # 尝试写入空字符串验证权限
|
||||
f.flush() # 立即刷新
|
||||
except Exception as e:
|
||||
logger.error(f"日志文件无法写入: {self.config.LOG_FILE}, 错误: {e}")
|
||||
raise
|
||||
|
||||
def _initialize_file_baseline(self):
|
||||
"""
|
||||
|
||||
@@ -957,9 +957,22 @@ def clear_report_log():
|
||||
"""
|
||||
try:
|
||||
log_file = settings.LOG_FILE
|
||||
with open(log_file, 'w', encoding='utf-8') as f:
|
||||
f.write('')
|
||||
|
||||
# 【修复】使用truncate而非重新打开,避免与logger的文件句柄冲突
|
||||
# 追加模式打开,然后truncate,保持文件句柄有效
|
||||
with open(log_file, 'r+', encoding='utf-8') as f:
|
||||
f.truncate(0) # 清空文件内容但不关闭文件
|
||||
f.flush() # 立即刷新
|
||||
|
||||
logger.info(f"已清空日志文件: {log_file}")
|
||||
except FileNotFoundError:
|
||||
# 文件不存在,创建空文件
|
||||
try:
|
||||
with open(log_file, 'w', encoding='utf-8') as f:
|
||||
f.write('')
|
||||
logger.info(f"创建日志文件: {log_file}")
|
||||
except Exception as e:
|
||||
logger.exception(f"创建日志文件失败: {str(e)}")
|
||||
except Exception as e:
|
||||
logger.exception(f"清空日志文件失败: {str(e)}")
|
||||
|
||||
@@ -969,29 +982,58 @@ def get_report_log():
|
||||
"""
|
||||
获取report.log内容,并按行去除空白返回。
|
||||
|
||||
【修复】优化大文件读取,添加错误处理和文件锁
|
||||
|
||||
返回:
|
||||
Response: JSON,包含最新日志行数组。
|
||||
"""
|
||||
try:
|
||||
log_file = settings.LOG_FILE
|
||||
|
||||
|
||||
if not os.path.exists(log_file):
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'log_lines': []
|
||||
})
|
||||
|
||||
with open(log_file, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
# 清理行尾的换行符
|
||||
|
||||
# 【修复】检查文件大小,避免读取过大文件导致内存问题
|
||||
file_size = os.path.getsize(log_file)
|
||||
max_size = 10 * 1024 * 1024 # 10MB限制
|
||||
|
||||
if file_size > max_size:
|
||||
# 文件过大,只读取最后10MB
|
||||
with open(log_file, 'rb') as f:
|
||||
f.seek(-max_size, 2) # 从文件末尾往前10MB
|
||||
# 跳过可能不完整的第一行
|
||||
f.readline()
|
||||
content = f.read().decode('utf-8', errors='replace')
|
||||
lines = content.splitlines()
|
||||
logger.warning(f"日志文件过大 ({file_size} bytes),仅返回最后 {max_size} bytes")
|
||||
else:
|
||||
# 正常大小,完整读取
|
||||
with open(log_file, 'r', encoding='utf-8', errors='replace') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
# 清理行尾的换行符和空行
|
||||
log_lines = [line.rstrip('\n\r') for line in lines if line.strip()]
|
||||
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'log_lines': log_lines
|
||||
})
|
||||
|
||||
|
||||
except PermissionError as e:
|
||||
logger.error(f"读取日志权限不足: {str(e)}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': '读取日志权限不足'
|
||||
}), 403
|
||||
except UnicodeDecodeError as e:
|
||||
logger.error(f"日志文件编码错误: {str(e)}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': '日志文件编码错误'
|
||||
}), 500
|
||||
except Exception as e:
|
||||
logger.exception(f"读取日志失败: {str(e)}")
|
||||
return jsonify({
|
||||
|
||||
Reference in New Issue
Block a user