fix: detect system_event_logout for real WeChat status

Previously only looked for WeChat send OK/error in last 200 log lines.
system_event_logout was missed, so dashboard showed 正常 when session
was actually dead. Now:
- Greps full webhook log for system_event_login/logout
- Logout after login => logged_out status
- WeChat send timeout (Hermes error) also counts as failure
This commit is contained in:
2026-08-02 04:32:37 +08:00
parent bd87a341dc
commit b0ad75b88e
+57 -11
View File
@@ -613,7 +613,7 @@ def _augment_wechat_status(entry):
r = subprocess.run(["tail", "-200", webhook_log],
capture_output=True, text=True, timeout=5)
lines = r.stdout.strip().split("\n") if r.stdout else []
last_ok, last_err, last_login = None, None, None
last_ok, last_err, last_login, last_logout = None, None, None, None
for line in reversed(lines):
ts_match = re.match(r"^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})", line)
ts = ts_match.group(1) if ts_match else None
@@ -621,17 +621,32 @@ def _augment_wechat_status(entry):
last_ok = ts
elif "WeChat send error" in line and ts and not last_err:
last_err = ts
if "system_event_login" in line and ts and not last_login:
last_login = ts
if last_ok and last_err and last_login:
if last_ok and last_err:
break
# Login/logout events: search full log (not just tail -200)
try:
for ev, key in (("system_event_logout", "last_logout"), ("system_event_login", "last_login")):
er = subprocess.run(["grep", ev, webhook_log],
capture_output=True, text=True, timeout=5)
ev_lines = [l for l in er.stdout.strip().split("\n") if l.strip()]
if ev_lines:
m = re.match(r"^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})", ev_lines[-1])
if m:
if key == "last_login":
last_login = m.group(1)
else:
last_logout = m.group(1)
except Exception:
pass
ok_dt = err_dt = None
ok_dt = err_dt = logout_dt = None
if last_ok:
ok_dt = datetime.strptime(last_ok, "%Y-%m-%d %H:%M:%S")
entry["session_age_hours"] = round((now - ok_dt).total_seconds() / 3600, 1)
if last_err:
err_dt = datetime.strptime(last_err, "%Y-%m-%d %H:%M:%S")
if last_logout:
logout_dt = datetime.strptime(last_logout, "%Y-%m-%d %H:%M:%S")
# Check docker logs for recent login (catches fresh logins before webhook sees them)
# Check webhook log for login event
@@ -642,7 +657,18 @@ def _augment_wechat_status(entry):
except ValueError:
pass
dlogin_dt = _docker_login_check()
if err_dt and login_dt and login_dt > err_dt:
# Logout is the most authoritative signal: if logout happened after the last login, session is dead
if logout_dt and login_dt and logout_dt > login_dt:
entry["login_ok"] = False
h = round((now - logout_dt).total_seconds() / 3600, 1)
entry["message"] = "已掉线" if h < 1 else f"已掉线 {h:.0f}h"
entry["status"] = "logged_out"
elif logout_dt and dlogin_dt and logout_dt > dlogin_dt:
entry["login_ok"] = False
h = round((now - logout_dt).total_seconds() / 3600, 1)
entry["message"] = "已掉线" if h < 1 else f"已掉线 {h:.0f}h"
entry["status"] = "logged_out"
elif err_dt and login_dt and login_dt > err_dt:
entry["login_ok"] = True
entry["session_age_hours"] = round((now - login_dt).total_seconds() / 3600, 1)
h = entry["session_age_hours"]
@@ -1757,10 +1783,20 @@ def api_wechat_status():
result["last_ok_at"] = ts
elif "WeChat send error" in line and ts and not result["last_err_at"]:
result["last_err_at"] = ts
if "system_event_login" in line and ts and not result.get("last_login_at"):
result["last_login_at"] = ts
if result["last_ok_at"] and result["last_err_at"] and result.get("last_login_at"):
if result["last_ok_at"] and result["last_err_at"]:
break
# Login/logout events: search full log (not just tail -200)
try:
for ev, key in (("system_event_logout", "last_logout_at"), ("system_event_login", "last_login_at")):
er = subprocess.run(["grep", ev, webhook_log],
capture_output=True, text=True, timeout=5)
ev_lines = [l for l in er.stdout.strip().split("\n") if l.strip()]
if ev_lines:
m = re.match(r"^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})", ev_lines[-1])
if m:
result[key] = m.group(1)
except Exception:
pass
except Exception:
pass
@@ -1772,12 +1808,14 @@ def api_wechat_status():
except ValueError:
pass
ok_dt = err_dt = None
ok_dt = err_dt = logout_dt = None
try:
if result["last_ok_at"]:
ok_dt = datetime.strptime(result["last_ok_at"], "%Y-%m-%d %H:%M:%S")
if result["last_err_at"]:
err_dt = datetime.strptime(result["last_err_at"], "%Y-%m-%d %H:%M:%S")
if result.get("last_logout_at"):
logout_dt = datetime.strptime(result["last_logout_at"], "%Y-%m-%d %H:%M:%S")
except ValueError:
pass
@@ -1790,7 +1828,15 @@ def api_wechat_status():
login_dt = datetime.strptime(result["last_login_at"], "%Y-%m-%d %H:%M:%S")
except ValueError:
pass
if err_dt and login_dt and login_dt > err_dt:
if logout_dt and login_dt and logout_dt > login_dt:
result["online"] = False
h = round((now - logout_dt).total_seconds() / 3600, 1)
result["message"] = "已掉线" if h < 1 else f"已掉线 {h:.0f}h 前"
elif logout_dt and dlogin_dt and logout_dt > dlogin_dt:
result["online"] = False
h = round((now - logout_dt).total_seconds() / 3600, 1)
result["message"] = "已掉线" if h < 1 else f"已掉线 {h:.0f}h 前"
elif err_dt and login_dt and login_dt > err_dt:
result["online"] = True
result["session_age_hours"] = round((now - login_dt).total_seconds() / 3600, 1)
h = result["session_age_hours"]