这段代码被你改的出现问题了,帮我检查
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from datetime import datetime, timedelta
import os
import re
# =============================
# 获取日志时间范围(用于 GUI 初始化等)
# =============================
def get_log_time_range(log_file):
min_time = None
max_time = None
try:
with open(log_file, 'r', encoding='utf-8') as file:
for line in file:
line = line.strip()
if not line:
continue
match = re.match(r'^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})', line)
if not match:
continue
time_str = match.group(1)
try:
log_time = datetime.strptime(time_str, '%Y-%m-%d %H:%M:%S')
if min_time is None or log_time < min_time:
min_time = log_time
if max_time is None or log_time > max_time:
max_time = log_time
except ValueError:
continue
except Exception as e:
messagebox.showerror("错误", f"读取日志时间范围失败:\n{e}")
return None, None
return min_time, max_time
# =============================
# 按时间和关键词过滤日志(原“日志翻译”功能)
# =============================
def filter_logs_by_time_and_keywords(log_file, start_time, end_time, keyword_dict):
matched_logs = []
try:
start_dt = datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S')
end_dt = datetime.strptime(end_time, '%Y-%m-%d %H:%M:%S')
except ValueError as e:
messagebox.showerror("错误", f"时间格式解析失败: {e}")
return matched_logs
if not os.path.exists(log_file):
messagebox.showerror("错误", "未找到指定的日志文件")
return matched_logs
try:
with open(log_file, 'r', encoding='utf-8') as file:
for line in file:
line = line.strip()
if not line:
continue
match = re.match(r'^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})', line)
if not match:
continue
time_str = match.group(1)
try:
log_time = datetime.strptime(time_str, '%Y-%m-%d %H:%M:%S')
if start_dt <= log_time <= end_dt:
for keyword, description in keyword_dict.items():
if keyword in line:
log_info = f"{time_str},{description},{line}"
matched_logs.append(log_info)
break
except ValueError:
continue
except Exception as e:
messagebox.showerror("读取错误", f"读取日志文件时出错:\n{e}")
return matched_logs
# =============================
# 加载 Keywords.txt 映射表
# =============================
def load_keywords():
keyword_dict = {}
keywords_file = 'Keywords.txt'
if not os.path.exists(keywords_file):
messagebox.showwarning("警告", f"未找到 {keywords_file} 文件,将使用空关键词表。")
return keyword_dict
try:
with open(keywords_file, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if line and '|||' in line:
keyword, desc = line.split('|||', 1)
keyword_dict[keyword.strip()] = desc.strip()
except Exception as e:
messagebox.showerror("错误", f"加载 Keywords.txt 失败:\n{e}")
return keyword_dict
# =============================
# 选择日志文件
# =============================
def select_log_file():
file_path = filedialog.askopenfilename(
title="选择日志文件",
filetypes=[("Text files", "*.txt"), ("Log files", "*.log"), ("All files", "*.*")]
)
if file_path:
log_file_entry.delete(0, tk.END)
log_file_entry.insert(0, file_path)
# =============================
# 执行日志翻译(按时间段 + 关键词过滤)
# =============================
def run_filter():
try:
start_input = f"{start_year.get()}-{start_month.get()}-{start_day.get()} {start_hour.get()}:{start_minute.get()}:{start_second.get()}"
end_input = f"{end_year.get()}-{end_month.get()}-{end_day.get()} {end_hour.get()}:{end_minute.get()}:{end_second.get()}"
start_dt = datetime.strptime(start_input, '%Y-%m-%d %H:%M:%S')
end_dt = datetime.strptime(end_input, '%Y-%m-%d %H:%M:%S')
except ValueError:
messagebox.showerror("输入错误", "时间格式无效,请检查年月日是否正确。")
return
log_file = log_file_entry.get().strip()
if not log_file or not os.path.exists(log_file):
messagebox.showerror("错误", "请提供有效的日志文件路径")
return
min_log_time, max_log_time = get_log_time_range(log_file)
if min_log_time is None or max_log_time is None:
messagebox.showwarning("警告", "无法从日志中提取任何有效时间信息。")
return
if end_dt < min_log_time or start_dt > max_log_time:
messagebox.showerror("无数据", "日志中不包含所选时间段")
return
keyword_dict = load_keywords()
result = filter_logs_by_time_and_keywords(log_file, start_input, end_input, keyword_dict)
if not result:
messagebox.showinfo("提示", "没有匹配的日志记录")
return
input_dir = os.path.dirname(log_file)
base_name = os.path.splitext(os.path.basename(log_file))[0]
output_file = os.path.join(input_dir, f"{base_name}-output.txt")
try:
with open(output_file, 'w', encoding='utf-8') as out_file:
for log in result:
parts = log.split(",", 2)
if len(parts) >= 3:
out_file.write(f"{parts[0]},{parts[1]}\n")
out_file.write(f"{parts[2]}\n")
messagebox.showinfo("完成", f"过滤完成!\n共找到 {len(result)} 条匹配日志\n已保存至:\n{output_file}")
except Exception as e:
messagebox.showerror("保存失败", f"无法保存文件:\n{e}")
# ===================================================
# 【新增】分析久坐提醒未触发的原因
# ===================================================
def analyze_longsit_issues(log_file, question_file_handle):
"""
分析久坐提醒相关的行为逻辑,并将每条结论+原始日志写入报告
使用直接写入方式,避免使用 findings 列表
"""
if not os.path.exists(log_file):
return
timestamp_pattern = r'^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})'
time_format = '%Y-%m-%d %H:%M:%S'
logs_with_time = [] # (datetime, line)
try:
with open(log_file, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line:
continue
match = re.match(timestamp_pattern, line)
if match:
try:
dt = datetime.strptime(match.group(1), time_format)
logs_with_time.append((dt, line))
except ValueError:
continue
except Exception as e:
messagebox.showerror("读取错误", f"读取日志失败:\n{e}")
return
# 提取所有与久坐相关的日志
longsit_logs = [
item for item in logs_with_time
if 'motion_longsit' in item[1] or 'motion_stand' in item[1]
]
if len(longsit_logs) == 0:
question_file_handle.write("\n\n🧩【久坐提醒分析】\n")
question_file_handle.write("⚠️ 未找到任何与久坐相关的日志(motion_longsit / motion_stand),无法分析。\n")
return
question_file_handle.write("\n\n🧩【久坐提醒分析】\n")
i = 0
while i < len(longsit_logs):
dt, line = longsit_logs[i]
time_str = dt.strftime('%H:%M:%S')
# ① 佩戴异常 → 计时清零
if 'Wear not on Wrist or the watch is charging' in line:
question_file_handle.write(f"• {time_str} 出现佩戴异常:手表未佩戴或正在充电 → 久坐计时清零\n")
question_file_handle.write(f" 📜 原始日志: {line}\n")
# ② 检测到站立 → 打断
elif 'motion_longsit:detect stand' in line:
question_file_handle.write(f"• {time_str} 检测到用户站立 → 久坐被打断\n")
question_file_handle.write(f" 📜 原始日志: {line}\n")
# ③ 中高活动量打断
elif 'current status is STANDING_STATUS type:3' in line:
question_file_handle.write(f"• {time_str} 检测到中高强度活动 → 自动打断久坐\n")
question_file_handle.write(f" 📜 原始日志: {line}\n")
# ④ 达到久坐阈值但未提醒?
elif 'motion_longsit:Motion long sit time ' in line:
match_time = re.search(r'motion long sit time (\d+)', line)
if not match_time:
i += 1
continue
duration = int(match_time.group(1))
if duration >= 60:
has_walking_suspect = False
has_remind_triggered = False
suspect_line = None
remind_line = None
j = i + 1
look_ahead = 0
while j < len(longsit_logs) and look_ahead < 20:
next_dt, next_line = longsit_logs[j]
# 走路嫌疑判定
if 'motion_longsit: suspect walking, statusCount =' in next_line:
count_match = re.search(r'statusCount = (\d+)', next_line)
if count_match and int(count_match.group(1)) > 10:
has_walking_suspect = True
suspect_line = next_line
# 是否触发提醒
if ('need to stand remind' in next_line or
'Motion remind report long sit' in next_line):
has_remind_triggered = True
remind_line = next_line
j += 1
look_ahead += 1
# 输出判断结果 + 日志证据
if has_walking_suspect and suspect_line:
next_time = re.match(r'^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})', suspect_line).group(1)[-8:]
question_file_handle.write(
f"• {time_str} 达到久坐阈值({duration}s),"
f"但在 {next_time} 被判定为疑似走路 → 取消提醒\n"
)
question_file_handle.write(f" 🕒 触发日志: {line}\n")
question_file_handle.write(f" 🚶 疑似走路: {suspect_line}\n")
elif has_remind_triggered and remind_line:
next_time = re.match(r'^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})', remind_line).group(1)[-8:]
question_file_handle.write(
f"• {time_str} 达到久坐阈值({duration}s),"
f"于 {next_time} 成功触发久坐提醒\n"
)
question_file_handle.write(f" 🕒 触发日志: {line}\n")
question_file_handle.write(f" 🔔 提醒上报: {remind_line}\n")
else:
question_file_handle.write(
f"• {time_str} 久坐时间已达 {duration} 秒,"
f"后续无‘走路嫌疑’也无‘提醒上报’记录 → ⚠️ 应触发提醒但未触发!需进一步排查\n"
)
question_file_handle.write(f" 📌 当前日志: {line}\n")
i += 1
# 写入结果
question_file_handle.write("\n\n🧩【久坐提醒分析】\n")
if findings:
for finding in findings:
question_file_handle.write(f"• {finding}\n")
else:
question_file_handle.write("✅ 未发现明显久坐提醒异常行为(但仍可能受限于日志完整性)\n")
# ===================================================
# 主函数:检查时间断层并生成问题报告
# ===================================================
def check_time_gap_and_generate_question(log_file):
if not os.path.exists(log_file):
messagebox.showerror("错误", "日志文件不存在")
return
timestamps = [] # 存储 (datetime, raw_line)
time_format = "%Y-%m-%d %H:%M:%S"
try:
with open(log_file, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line:
continue
match = re.match(r'^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})', line)
if not match:
continue
time_str = match.group(1)
try:
dt = datetime.strptime(time_str, time_format)
timestamps.append((dt, line))
except ValueError:
continue
except Exception as e:
messagebox.showerror("错误", f"读取日志文件失败:\n{e}")
return
if len(timestamps) < 2:
messagebox.showinfo("提示", "日志中少于两条有效时间记录,无法检测断层")
return
# 排序确保顺序
timestamps.sort(key=lambda x: x[0])
missing_periods = []
# ⚙️ 可调参数:判定为“异常断层”的最小间隔(单位:秒)
GAP_THRESHOLD = 60
for i in range(len(timestamps) - 1):
current_time = timestamps[i][0]
next_time = timestamps[i + 1][0]
gap_seconds = (next_time - current_time).total_seconds()
if gap_seconds > GAP_THRESHOLD:
start_missing = current_time + timedelta(seconds=1)
end_missing = next_time - timedelta(seconds=1)
if start_missing <= end_missing:
missing_periods.append({
'start': start_missing,
'end': end_missing,
'gap_seconds': int(gap_seconds),
'before_log': timestamps[i][1][:100],
'after_log': timestamps[i+1][1][:100]
})
# 输出文件路径
output_dir = os.path.dirname(log_file)
question_file = os.path.join(output_dir, "Log_Question.txt")
with open(question_file, "w", encoding="utf-8") as f:
if missing_periods:
f.write("该日志存在长时间无更新的时间段(超过60秒未记录日志),具体如下:\n\n")
for period in missing_periods:
f.write(f"⚠️ 时间断层:从 {period['start'].strftime('%Y-%m-%d %H:%M:%S')} "
f"到 {period['end'].strftime('%Y-%m-%d %H:%M:%S')} "
f"(共缺失 {period['gap_seconds']} 秒)\n")
f.write(f" 上一条日志: {period['before_log']}\n")
f.write(f" 下一条日志: {period['after_log']}\n\n")
else:
f.write("✅ 日志时间分布合理,无显著断层。\n\n")
# =================== 调用久坐提醒分析 ===================
analyze_longsit_issues(log_file, f)
messagebox.showwarning(
"⚠️ 分析完成",
f"问题分析已完成,详情见:\n{question_file}"
)
# =====================================
# 执行时间断层检测(按钮回调)
# =====================================
def run_time_check():
log_file = log_file_entry.get().strip()
if not log_file:
messagebox.showerror("错误", "请先选择日志文件")
return
if not os.path.exists(log_file):
messagebox.showerror("错误", "日志文件不存在,请检查路径")
return
check_time_gap_and_generate_question(log_file)
# =============================
# 创建主窗口
# =============================
root = tk.Tk()
root.title("日志分析工具")
root.geometry("650x400")
root.resizable(False, False)
# === 当前时间初始化 ===
now = datetime.now()
default_year = now.year
default_month = now.month
default_day = now.day
# === 日志文件选择区域 ===
log_frame = ttk.Frame(root, padding="10")
log_frame.pack(pady=10, fill=tk.X)
ttk.Label(log_frame, text="日志文件路径:").grid(row=0, column=0, sticky=tk.W, padx=(0, 5))
log_file_entry = ttk.Entry(log_frame, width=50)
log_file_entry.grid(row=0, column=1, padx=5)
ttk.Button(log_frame, text="浏览...", command=select_log_file).grid(row=0, column=2, padx=(5, 0))
# === 时间选择区域 ===
time_frame = ttk.LabelFrame(root, text="时间范围设置", padding="10")
time_frame.pack(padx=20, pady=10, fill=tk.X)
row_start = 0
row_end = 1
# --- 开始时间 ---
ttk.Label(time_frame, text="开始时间:").grid(row=row_start, column=0, sticky=tk.E, padx=(0, 5), pady=2)
start_year = tk.StringVar(value=default_year)
start_month = tk.StringVar(value=f"{default_month:02d}")
start_day = tk.StringVar(value=f"{default_day:02d}")
start_hour = tk.StringVar(value="00")
start_minute = tk.StringVar(value="00")
start_second = tk.StringVar(value="00")
ttk.Spinbox(time_frame, from_=2000, to=2100, textvariable=start_year, width=6).grid(row=row_start, column=1, padx=2)
ttk.Label(time_frame, text="-").grid(row=row_start, column=2)
ttk.Spinbox(time_frame, from_=1, to=12, textvariable=start_month, width=4).grid(row=row_start, column=3, padx=2)
ttk.Label(time_frame, text="-").grid(row=row_start, column=4)
ttk.Spinbox(time_frame, from_=1, to=31, textvariable=start_day, width=4).grid(row=row_start, column=5, padx=2)
ttk.Label(time_frame, text=" ").grid(row=row_start, column=6)
ttk.Spinbox(time_frame, from_=0, to=23, textvariable=start_hour, width=4).grid(row=row_start, column=7, padx=2)
ttk.Label(time_frame, text=":").grid(row=row_start, column=8)
ttk.Spinbox(time_frame, from_=0, to=59, textvariable=start_minute, width=4).grid(row=row_start, column=9, padx=2)
ttk.Label(time_frame, text=":").grid(row=row_start, column=10)
ttk.Spinbox(time_frame, from_=0, to=59, textvariable=start_second, width=4).grid(row=row_start, column=11, padx=2)
# --- 结束时间 ---
ttk.Label(time_frame, text="结束时间:").grid(row=row_end, column=0, sticky=tk.E, padx=(0, 5), pady=2)
end_year = tk.StringVar(value=default_year)
end_month = tk.StringVar(value=f"{default_month:02d}")
end_day = tk.StringVar(value=f"{default_day:02d}")
end_hour = tk.StringVar(value="23")
end_minute = tk.StringVar(value="59")
end_second = tk.StringVar(value="59")
ttk.Spinbox(time_frame, from_=2000, to=2100, textvariable=end_year, width=6).grid(row=row_end, column=1, padx=2)
ttk.Label(time_frame, text="-").grid(row=row_end, column=2)
ttk.Spinbox(time_frame, from_=1, to=12, textvariable=end_month, width=4).grid(row=row_end, column=3, padx=2)
ttk.Label(time_frame, text="-").grid(row=row_end, column=4)
ttk.Spinbox(time_frame, from_=1, to=31, textvariable=end_day, width=4).grid(row=row_end, column=5, padx=2)
ttk.Label(time_frame, text=" ").grid(row=row_end, column=6)
ttk.Spinbox(time_frame, from_=0, to=23, textvariable=end_hour, width=4).grid(row=row_end, column=7, padx=2)
ttk.Label(time_frame, text=":").grid(row=row_end, column=8)
ttk.Spinbox(time_frame, from_=0, to=59, textvariable=end_minute, width=4).grid(row=row_end, column=9, padx=2)
ttk.Label(time_frame, text=":").grid(row=row_end, column=10)
ttk.Spinbox(time_frame, from_=0, to=59, textvariable=end_second, width=4).grid(row=row_end, column=11, padx=2)
# === 功能按钮 ===
run_button = ttk.Button(root, text="🔍 开始日志翻译", command=run_filter)
run_button.pack(pady=10)
check_button = ttk.Button(root, text="⏱️ 问题定位", command=run_time_check)
check_button.pack(pady=5)
# 启动主循环
root.mainloop()