improvement(log): 补充聊天消息与会话加载阶段日志

- 为会话加载、刷新和联系人初始化补充阶段性 trace
- 为消息加载、归一化、状态提交和媒体刷新补充耗时日志
- 便于定位聊天页卡顿和加载异常
This commit is contained in:
2977094657
2026-04-14 00:20:36 +08:00
Unverified
parent 7458762b2b
commit a1c99c8704
2 changed files with 127 additions and 32 deletions
+41 -32
View File
@@ -5,6 +5,7 @@ import {
getVoiceDurationInSeconds,
getVoiceWidth
} from '~/lib/chat/formatters'
import { createPerfTrace } from '~/lib/chat/perf-logger'
import { createMessageNormalizer, dedupeMessagesById } from '~/lib/chat/message-normalizer'
export const useChatMessages = ({
@@ -410,9 +411,15 @@ export const useChatMessages = ({
const loadMessages = async ({ username, reset }) => {
if (!username || !selectedAccount.value) return
logMessagePhase('loadMessages:enter', {
username,
reset
const trace = createPerfTrace('chat-messages', {
account: String(selectedAccount.value || '').trim(),
selectedUsername: String(selectedContact.value?.username || '').trim(),
username: String(username || '').trim(),
reset: !!reset
})
trace.log('loadMessages:enter', {
activeMessagesFor: String(activeMessagesFor.value || '').trim()
})
messagesError.value = ''
isLoadingMessages.value = true
@@ -438,46 +445,37 @@ export const useChatMessages = ({
if (realtimeEnabled.value) {
params.source = 'realtime'
}
logMessagePhase('loadMessages:request:start', {
username,
reset,
trace.log('loadMessages:request:start', {
offset,
existingCount: existing.length,
renderTypeFilter: messageTypeFilter.value,
realtime: !!realtimeEnabled.value
})
const response = await api.listChatMessages(params)
logMessagePhase('loadMessages:request:end', {
username,
reset,
trace.log('loadMessages:request:end', {
rawCount: Array.isArray(response?.messages) ? response.messages.length : 0,
total: Number(response?.total || 0),
hasMore: response?.hasMore
})
const raw = response?.messages || []
logMessagePhase('loadMessages:normalize:start', {
username,
trace.log('loadMessages:normalize:start', {
rawCount: raw.length
})
const mapped = dedupeMessagesById(raw.map(normalizeMessage))
logMessagePhase('loadMessages:normalize:end', {
username,
trace.log('loadMessages:normalize:end', {
mappedCount: mapped.length,
renderTypeCounts: summarizeRenderTypes(mapped)
})
if (activeMessagesFor.value !== username) {
logMessagePhase('loadMessages:abort-stale', {
username,
trace.log('loadMessages:abort-stale', {
activeMessagesFor: activeMessagesFor.value
})
return
}
logMessagePhase('loadMessages:state-commit:start', {
username,
reset,
trace.log('loadMessages:state-commit:start', {
mappedCount: mapped.length
})
if (reset) {
@@ -496,8 +494,7 @@ export const useChatMessages = ({
[username]: [...older, ...existing]
}
}
logMessagePhase('loadMessages:state-commit:end', {
username,
trace.log('loadMessages:state-commit:end', {
storedCount: (allMessages.value[username] || []).length
})
@@ -508,18 +505,14 @@ export const useChatMessages = ({
hasMore: response?.hasMore
}
}
logMessagePhase('loadMessages:meta-commit:end', {
username,
trace.log('loadMessages:meta-commit:end', {
total: Number(response?.total || 0),
hasMore: response?.hasMore
})
logMessagePhase('loadMessages:nextTick:start', {
username
})
trace.log('loadMessages:nextTick:start')
await nextTick()
logMessagePhase('loadMessages:nextTick:end', {
username,
trace.log('loadMessages:nextTick:end', {
renderedCount: (allMessages.value[username] || []).length
})
const nextContainer = messageContainerRef.value
@@ -532,13 +525,16 @@ export const useChatMessages = ({
}
}
updateJumpToBottomState()
logMessagePhase('loadMessages:scroll:end', {
username,
trace.log('loadMessages:scroll:end', {
hasContainer: !!nextContainer,
scrollTop: nextContainer ? nextContainer.scrollTop : null,
scrollHeight: nextContainer ? nextContainer.scrollHeight : null
})
} catch (error) {
trace.log('loadMessages:error', {
message: String(error?.message || ''),
errorName: String(error?.name || '')
})
console.error('[chat-messages] loadMessages:error', {
account: String(selectedAccount.value || '').trim(),
username: String(username || '').trim(),
@@ -548,9 +544,7 @@ export const useChatMessages = ({
messagesError.value = error?.message || '加载聊天记录失败'
} finally {
isLoadingMessages.value = false
logMessagePhase('loadMessages:exit', {
username,
reset,
trace.log('loadMessages:exit', {
loading: isLoadingMessages.value,
error: messagesError.value
})
@@ -571,9 +565,24 @@ export const useChatMessages = ({
const refreshCurrentMessageMedia = async () => {
if (!selectedContact.value?.username) return
const trace = createPerfTrace('chat-messages', {
account: String(selectedAccount.value || '').trim(),
username: String(selectedContact.value?.username || '').trim(),
action: 'refreshCurrentMessageMedia'
})
trace.log('refreshCurrentMessageMedia:start', {
localMediaVersion: Number(localMediaVersion.value || 0)
})
bumpLocalMediaVersion()
trace.log('refreshCurrentMessageMedia:version-bumped', {
localMediaVersion: Number(localMediaVersion.value || 0)
})
renormalizeLoadedMessages(selectedContact.value.username)
trace.log('refreshCurrentMessageMedia:renormalized', {
renderedCount: (allMessages.value[selectedContact.value.username] || []).length
})
await nextTick()
trace.log('refreshCurrentMessageMedia:end')
}
const refreshRealtimeIncremental = async () => {
@@ -1,5 +1,6 @@
import { computed, onMounted, ref } from 'vue'
import { normalizeSessionPreview } from '~/lib/chat/formatters'
import { createPerfTrace } from '~/lib/chat/perf-logger'
const SESSION_LIST_WIDTH_KEY = 'ui.chat.session_list_width_physical'
const SESSION_LIST_WIDTH_KEY_LEGACY = 'ui.chat.session_list_width'
@@ -170,6 +171,14 @@ export const useChatSessions = ({ chatAccounts, selectedAccount, realtimeEnabled
return []
}
const trace = createPerfTrace('chat-sessions', {
account: String(selectedAccount.value || '').trim(),
action: 'loadSessionsForSelectedAccount'
})
trace.log('loadSessions:start', {
realtimeEnabled: !!realtimeEnabled?.value
})
const fetchSessions = async (source) => {
const params = {
account: selectedAccount.value,
@@ -184,18 +193,38 @@ export const useChatSessions = ({ chatAccounts, selectedAccount, realtimeEnabled
let sessionsResp = null
if (realtimeEnabled?.value) {
try {
trace.log('loadSessions:request:start', {
source: 'realtime'
})
sessionsResp = await fetchSessions('realtime')
trace.log('loadSessions:request:end', {
source: 'realtime',
rawCount: Array.isArray(sessionsResp?.sessions) ? sessionsResp.sessions.length : 0
})
} catch {
sessionsResp = null
trace.log('loadSessions:request:error', {
source: 'realtime'
})
}
}
if (!sessionsResp) {
trace.log('loadSessions:request:start', {
source: 'default'
})
sessionsResp = await fetchSessions('')
trace.log('loadSessions:request:end', {
source: 'default',
rawCount: Array.isArray(sessionsResp?.sessions) ? sessionsResp.sessions.length : 0
})
}
const sessions = Array.isArray(sessionsResp?.sessions) ? sessionsResp.sessions : []
contacts.value = mapSessions(sessions)
contactsError.value = ''
trace.log('loadSessions:end', {
contactCount: contacts.value.length
})
return contacts.value
}
@@ -208,6 +237,14 @@ export const useChatSessions = ({ chatAccounts, selectedAccount, realtimeEnabled
const desiredSource = (sourceOverride != null)
? String(sourceOverride || '').trim()
: (realtimeEnabled?.value ? 'realtime' : '')
const trace = createPerfTrace('chat-sessions', {
account: String(selectedAccount.value || '').trim(),
action: 'refreshSessionsForSelectedAccount',
desiredSource
})
trace.log('refreshSessions:start', {
previousUsername
})
const params = {
account: selectedAccount.value,
@@ -219,15 +256,35 @@ export const useChatSessions = ({ chatAccounts, selectedAccount, realtimeEnabled
let sessionsResp = null
if (desiredSource) {
try {
trace.log('refreshSessions:request:start', {
source: desiredSource
})
sessionsResp = await api.listChatSessions({ ...params, source: desiredSource })
trace.log('refreshSessions:request:end', {
source: desiredSource,
rawCount: Array.isArray(sessionsResp?.sessions) ? sessionsResp.sessions.length : 0
})
} catch {
sessionsResp = null
trace.log('refreshSessions:request:error', {
source: desiredSource
})
}
}
if (!sessionsResp) {
try {
trace.log('refreshSessions:request:start', {
source: 'default'
})
sessionsResp = await api.listChatSessions(params)
trace.log('refreshSessions:request:end', {
source: 'default',
rawCount: Array.isArray(sessionsResp?.sessions) ? sessionsResp.sessions.length : 0
})
} catch {
trace.log('refreshSessions:request:error', {
source: 'default'
})
return
}
}
@@ -240,6 +297,10 @@ export const useChatSessions = ({ chatAccounts, selectedAccount, realtimeEnabled
const matched = nextContacts.find((contact) => contact.username === previousUsername)
if (matched) selectedContact.value = matched
}
trace.log('refreshSessions:end', {
contactCount: nextContacts.length,
selectedUsername: String(selectedContact.value?.username || '').trim()
})
}
const loadContacts = async () => {
@@ -249,25 +310,50 @@ export const useChatSessions = ({ chatAccounts, selectedAccount, realtimeEnabled
isLoadingContacts.value = true
contactsError.value = ''
const trace = createPerfTrace('chat-sessions', {
account: String(selectedAccount.value || '').trim(),
action: 'loadContacts'
})
trace.log('loadContacts:start', {
cachedContacts: contacts.value.length
})
try {
const hadLoadedAccountSnapshot = !!chatAccounts.loaded
await chatAccounts.ensureLoaded()
trace.log('loadContacts:accounts-ready', {
hadLoadedAccountSnapshot,
availableAccounts: Array.isArray(chatAccounts?.accounts) ? chatAccounts.accounts.length : 0
})
if (!selectedAccount.value && hadLoadedAccountSnapshot) {
await chatAccounts.ensureLoaded({ force: true })
trace.log('loadContacts:accounts-refreshed')
}
if (!selectedAccount.value) {
clearContactsState(chatAccounts.error || '未检测到已解密账号,请先解密数据库。')
trace.log('loadContacts:no-account', {
error: contactsError.value
})
return { usedPrefetched: false }
}
await loadSessionsForSelectedAccount()
trace.log('loadContacts:end', {
contactCount: contacts.value.length
})
return { usedPrefetched: false }
} catch (error) {
clearContactsState(error?.message || '加载联系人失败')
trace.log('loadContacts:error', {
message: String(error?.message || '')
})
return { usedPrefetched: false }
} finally {
isLoadingContacts.value = false
trace.log('loadContacts:exit', {
loading: isLoadingContacts.value,
error: contactsError.value
})
}
}