7373from ..database_filters import list_countable_database_names
7474from ..key_store import remove_account_keys_from_store
7575from ..path_fix import PathFixRoute
76+ from ..perf_trace import create_perf_trace
7677from ..session_last_message import (
7778 build_session_last_message_table ,
7879 get_session_last_message_status ,
@@ -3998,6 +3999,17 @@ def list_chat_sessions(
39983999 contact_db_path = account_dir / "contact.db"
39994000 head_image_db_path = account_dir / "head_image.db"
40004001 base_url = str (request .base_url ).rstrip ("/" )
4002+ _trace_id , trace = create_perf_trace (
4003+ logger ,
4004+ "chat.sessions" ,
4005+ account = account_dir .name ,
4006+ source = source_norm or "default" ,
4007+ limit = int (limit ),
4008+ includeHidden = bool (include_hidden ),
4009+ includeOfficial = bool (include_official ),
4010+ preview = str (preview or "" ),
4011+ )
4012+ trace ("request:start" )
40014013
40024014 rt_conn = None
40034015 rows : list [Any ]
@@ -4122,6 +4134,12 @@ def _ts(v: Any) -> int:
41224134 finally :
41234135 sconn .close ()
41244136
4137+ trace (
4138+ "rows:loaded" ,
4139+ rawCount = len (rows or []),
4140+ realtime = bool (source_norm == "realtime" ),
4141+ )
4142+
41254143 filtered : list [Any ] = []
41264144 for r in rows :
41274145 username = _session_row_get (r , "username" , "" ) or ""
@@ -4133,8 +4151,18 @@ def _ts(v: Any) -> int:
41334151 continue
41344152 filtered .append (r )
41354153
4154+ trace (
4155+ "rows:filtered" ,
4156+ filteredCount = len (filtered ),
4157+ )
4158+
41364159 raw_usernames = [str (_session_row_get (r , "username" , "" ) or "" ).strip () for r in filtered ]
41374160 top_flags = _load_contact_top_flags (contact_db_path , raw_usernames )
4161+ trace (
4162+ "top-flags:loaded" ,
4163+ usernameCount = len (raw_usernames ),
4164+ topCount = sum (1 for value in top_flags .values () if value ),
4165+ )
41384166
41394167 def _to_int (v : Any ) -> int :
41404168 try :
@@ -4164,6 +4192,12 @@ def _session_sort_key(row: Any) -> tuple[int, int, int]:
41644192
41654193 contact_rows = _load_contact_rows (contact_db_path , usernames )
41664194 local_avatar_usernames = _query_head_image_usernames (head_image_db_path , usernames )
4195+ trace (
4196+ "contacts:loaded" ,
4197+ usernameCount = len (usernames ),
4198+ contactRowCount = len (contact_rows ),
4199+ localAvatarCount = len (local_avatar_usernames ),
4200+ )
41674201
41684202 # Some sessions (notably enterprise groups / openim-related IDs) may be missing from decrypted contact.db
41694203 # (or lack nickname/avatar columns). In that case, fall back to WCDB APIs (same as WeFlow) to resolve
@@ -4212,6 +4246,12 @@ def _session_sort_key(row: Any) -> tuple[int, int, int]:
42124246 wcdb_display_names = {}
42134247 wcdb_avatar_urls = {}
42144248
4249+ trace (
4250+ "wcdb-fallback:loaded" ,
4251+ displayNameCount = len (wcdb_display_names ),
4252+ avatarUrlCount = len (wcdb_avatar_urls ),
4253+ )
4254+
42154255 preview_mode = str (preview or "" ).strip ().lower ()
42164256 if preview_mode not in {"latest" , "index" , "session" , "db" , "none" }:
42174257 preview_mode = "latest"
@@ -4299,6 +4339,14 @@ def _is_generic_location_preview(value: Any) -> bool:
42994339 except Exception :
43004340 pass
43014341
4342+ trace (
4343+ "previews:resolved" ,
4344+ previewMode = preview_mode ,
4345+ previewCount = len (last_previews ),
4346+ groupSenderDisplayCount = len (group_sender_display_names ),
4347+ unresolvedGroupSenderCount = len (unresolved ),
4348+ )
4349+
43024350 sessions : list [dict [str , Any ]] = []
43034351 for r in filtered :
43044352 username = r ["username" ]
@@ -4416,6 +4464,10 @@ def _is_generic_location_preview(value: Any) -> bool:
44164464 }
44174465 )
44184466
4467+ trace (
4468+ "response:ready" ,
4469+ sessionCount = len (sessions ),
4470+ )
44194471 return {
44204472 "status" : "success" ,
44214473 "account" : account_dir .name ,
@@ -5169,11 +5221,24 @@ def list_chat_messages(
51695221 head_image_db_path = account_dir / "head_image.db"
51705222 message_resource_db_path = account_dir / "message_resource.db"
51715223 base_url = str (request .base_url ).rstrip ("/" )
5224+ _trace_id , trace = create_perf_trace (
5225+ logger ,
5226+ "chat.messages" ,
5227+ account = account_dir .name ,
5228+ username = username ,
5229+ source = source_norm or "default" ,
5230+ limit = int (limit ),
5231+ offset = int (offset ),
5232+ order = str (order or "" ),
5233+ renderTypes = str (render_types or "" ),
5234+ )
5235+ trace ("request:start" )
51725236
51735237 db_paths : list [Path ] = []
51745238 if source_norm != "realtime" :
51755239 db_paths = _iter_message_db_paths (account_dir )
51765240 if not db_paths :
5241+ trace ("response:error" , reason = "no-message-dbs" )
51775242 return {
51785243 "status" : "error" ,
51795244 "account" : account_dir .name ,
@@ -5199,6 +5264,12 @@ def list_chat_messages(
51995264 resource_conn = None
52005265 resource_chat_id = None
52015266
5267+ trace (
5268+ "resource-db:resolved" ,
5269+ hasResourceDb = bool (resource_conn is not None ),
5270+ resourceChatId = int (resource_chat_id or 0 ),
5271+ )
5272+
52025273 want_asc = str (order or "" ).lower () != "desc"
52035274
52045275 want_types : Optional [set [str ]] = None
@@ -5337,6 +5408,16 @@ def pick(*keys: str) -> Any:
53375408 break
53385409 scan_take = next_take
53395410
5411+ trace (
5412+ "messages:collected" ,
5413+ scanTake = int (scan_take ),
5414+ mergedCount = len (merged ),
5415+ hasMoreAny = bool (has_more_any ),
5416+ senderUsernameCount = len (sender_usernames ),
5417+ quoteUsernameCount = len (quote_usernames ),
5418+ patUsernameCount = len (pat_usernames ),
5419+ )
5420+
53405421 # Self-heal (default source only): if the decrypted snapshot has no conversation table yet (new session),
53415422 # do a one-shot realtime->decrypted sync and re-query once. This avoids "暂无聊天记录" after turning off realtime.
53425423 if (
@@ -5352,6 +5433,7 @@ def pick(*keys: str) -> Any:
53525433 missing_table = True
53535434
53545435 if missing_table :
5436+ trace ("self-heal:missing-table" )
53555437 rt_conn2 = None
53565438 try :
53575439 rt_conn2 = WCDB_REALTIME .ensure_connected (account_dir )
@@ -5362,6 +5444,7 @@ def pick(*keys: str) -> Any:
53625444
53635445 if rt_conn2 is not None :
53645446 try :
5447+ trace ("self-heal:sync:start" )
53655448 with _realtime_sync_lock (account_dir .name , username ):
53665449 msg_db_path2 , table_name2 = _ensure_decrypted_message_table (account_dir , username )
53675450 _sync_chat_realtime_messages_for_table (
@@ -5373,7 +5456,9 @@ def pick(*keys: str) -> Any:
53735456 max_scan = max (200 , int (limit ) + 50 ),
53745457 backfill_limit = 0 ,
53755458 )
5459+ trace ("self-heal:sync:end" )
53765460 except Exception :
5461+ trace ("self-heal:sync:error" )
53775462 pass
53785463
53795464 (
@@ -5393,6 +5478,11 @@ def pick(*keys: str) -> Any:
53935478 )
53945479 if want_types is not None :
53955480 merged = [m for m in merged if _normalize_render_type_key (m .get ("renderType" )) in want_types ]
5481+ trace (
5482+ "self-heal:requery:end" ,
5483+ mergedCount = len (merged ),
5484+ hasMoreAny = bool (has_more_any ),
5485+ )
53965486
53975487 r"""
53985488 take = int(limit) + int(offset)
@@ -5886,8 +5976,17 @@ def sort_key(m: dict[str, Any]) -> tuple[int, int, int]:
58865976 if want_asc :
58875977 page = list (reversed (page ))
58885978
5979+ trace (
5980+ "page:sliced" ,
5981+ mergedCount = len (merged ),
5982+ pageCount = len (page ),
5983+ hasMore = bool (has_more_global ),
5984+ orderAsc = bool (want_asc ),
5985+ )
5986+
58895987 # Hot path optimization: only enrich the page we return.
58905988 if not page :
5989+ trace ("response:ready" , pageCount = 0 )
58915990 return {
58925991 "status" : "success" ,
58935992 "account" : account_dir .name ,
@@ -5961,6 +6060,12 @@ def sort_key(m: dict[str, Any]) -> tuple[int, int, int]:
59616060 )
59626061 sender_contact_rows = _load_contact_rows (contact_db_path , uniq_senders )
59636062 local_sender_avatars = _query_head_image_usernames (head_image_db_path , uniq_senders )
6063+ trace (
6064+ "senders:loaded" ,
6065+ uniqSenderCount = len (uniq_senders ),
6066+ senderContactRowCount = len (sender_contact_rows ),
6067+ localSenderAvatarCount = len (local_sender_avatars ),
6068+ )
59646069
59656070 # contact.db may not include enterprise/openim contacts (or group chatroom records). WCDB has a more complete
59666071 # view of display names + avatar URLs, so we use it as a best-effort fallback.
@@ -5997,6 +6102,12 @@ def sort_key(m: dict[str, Any]) -> tuple[int, int, int]:
59976102 chatroom_id = username ,
59986103 sender_usernames = uniq_senders ,
59996104 )
6105+ trace (
6106+ "sender-fallbacks:loaded" ,
6107+ wcdbDisplayNameCount = len (wcdb_display_names ),
6108+ wcdbAvatarUrlCount = len (wcdb_avatar_urls ),
6109+ groupNicknameCount = len (group_nicknames ),
6110+ )
60006111
60016112 for m in messages_window :
60026113 # If appmsg doesn't provide sourcedisplayname, try mapping sourceusername to display name.
@@ -6155,6 +6266,12 @@ def sort_key(m: dict[str, Any]) -> tuple[int, int, int]:
61556266 wcdb_display_names = wcdb_display_names ,
61566267 )
61576268
6269+ trace (
6270+ "response:ready" ,
6271+ pageCount = len (page ),
6272+ total = int (offset ) + len (page ) + (1 if has_more_global else 0 ),
6273+ hasMore = bool (has_more_global ),
6274+ )
61586275 return {
61596276 "status" : "success" ,
61606277 "account" : account_dir .name ,
0 commit comments