mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-08-18 12:07:01 +08:00
chore: 添加应用诊断日志
This commit is contained in:
@@ -3854,6 +3854,18 @@ body {
|
||||
margin: 4px 0 10px;
|
||||
}
|
||||
|
||||
.report-task-error button + button {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.report-task-log-path {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
overflow-wrap: anywhere;
|
||||
color: var(--wxex-text-secondary);
|
||||
font: 11px/16px var(--wxex-font);
|
||||
}
|
||||
|
||||
@media (max-width: 1120px) {
|
||||
.report-page {
|
||||
grid-template-columns: 268px minmax(360px, 1fr) 280px;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { ReportGenerationPhase } from '../../hooks/useGroupReportGeneration'
|
||||
|
||||
interface ReportTaskStatusPanelProps {
|
||||
@@ -27,6 +27,14 @@ export function ReportTaskStatusPanel({
|
||||
}: ReportTaskStatusPanelProps): React.ReactElement {
|
||||
const activeIndex = phaseIndex(phase)
|
||||
const completedAll = phase === 'success'
|
||||
const [logPath, setLogPath] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
void window.api
|
||||
.getAppLogPath()
|
||||
.then(setLogPath)
|
||||
.catch(() => undefined)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<aside className="report-task-panel">
|
||||
@@ -70,6 +78,10 @@ export function ReportTaskStatusPanel({
|
||||
<button type="button" onClick={onRetry}>
|
||||
重试
|
||||
</button>
|
||||
<button type="button" onClick={() => void window.api.revealAppLog()}>
|
||||
打开诊断日志
|
||||
</button>
|
||||
{logPath && <small className="report-task-log-path">{logPath}</small>}
|
||||
</div>
|
||||
)}
|
||||
{phase === 'success' && (
|
||||
|
||||
@@ -108,6 +108,31 @@ const withTimeout = async <T>(
|
||||
const errorMessage = (error: unknown): string =>
|
||||
error instanceof Error ? error.message : String(error)
|
||||
|
||||
const writeReportLog = (
|
||||
level: 'info' | 'warn' | 'error',
|
||||
message: string,
|
||||
details?: Record<string, unknown>
|
||||
): void => {
|
||||
void window.api
|
||||
.writeAppLog({ level, scope: 'group-report', message, details })
|
||||
.catch(() => undefined)
|
||||
}
|
||||
|
||||
const jsonErrorContext = (raw: string, error: unknown): Record<string, unknown> => {
|
||||
const message = errorMessage(error)
|
||||
const position = Number(/\bposition\s+(\d+)/i.exec(message)?.[1])
|
||||
const safePosition = Number.isFinite(position) ? Math.max(0, Math.min(raw.length, position)) : 0
|
||||
return {
|
||||
error: message,
|
||||
outputLength: raw.length,
|
||||
position: Number.isFinite(position) ? position : undefined,
|
||||
context:
|
||||
raw.length && Number.isFinite(position)
|
||||
? raw.slice(Math.max(0, safePosition - 300), Math.min(raw.length, safePosition + 300))
|
||||
: raw.slice(0, 600)
|
||||
}
|
||||
}
|
||||
|
||||
const isGroupContact = (contact: Contact | null): boolean =>
|
||||
Boolean(contact?.type === 'group' || contact?.m_nsUsrName?.endsWith('@chatroom'))
|
||||
|
||||
@@ -358,6 +383,7 @@ export function useGroupReportGeneration({
|
||||
}
|
||||
|
||||
const startGenerateTime = Date.now()
|
||||
let failedAt = '初始化'
|
||||
const logs: ReportGenerationLog[] = []
|
||||
const pushLog = (log: ReportGenerationLog): void => {
|
||||
logs.push(log)
|
||||
@@ -388,15 +414,29 @@ export function useGroupReportGeneration({
|
||||
modelName: modelConfig.model,
|
||||
generationLogs: []
|
||||
})
|
||||
writeReportLog('info', '开始生成群聊日报', {
|
||||
groupName: sourceContact.m_nsNickName || sourceContact.m_nsUsrName,
|
||||
dateRange: summaryDateRange,
|
||||
selectedMessageTypes: summaryMessageTypes,
|
||||
providerName: modelConfig.providerName,
|
||||
model: modelConfig.model,
|
||||
templateId
|
||||
})
|
||||
|
||||
try {
|
||||
failedAt = '读取聊天记录'
|
||||
const sourceMessages = await trackStep('读取聊天记录', () => loadRangeMessages(true))
|
||||
|
||||
const selectedTypes = selectedMessageTypeSet(summaryMessageTypes)
|
||||
const filteredMessages = sourceMessages.filter((message) => selectedTypes.has(message.type))
|
||||
if (!filteredMessages.length) throw new Error('当前范围没有可总结消息')
|
||||
writeReportLog('info', '聊天记录读取完成', {
|
||||
sourceMessageCount: sourceMessages.length,
|
||||
filteredMessageCount: filteredMessages.length
|
||||
})
|
||||
|
||||
setPhase('preparingInput')
|
||||
failedAt = '整理日报输入'
|
||||
const input = await trackStep('整理输入', async () => {
|
||||
const namedReportMessages = await applyGroupMemberNames(
|
||||
sourceContact,
|
||||
@@ -407,6 +447,7 @@ export function useGroupReportGeneration({
|
||||
})
|
||||
|
||||
setPhase('requestingModel')
|
||||
failedAt = '调用模型生成内容'
|
||||
const aiMessages = [
|
||||
{ role: 'system', content: GROUP_REPORT_SYSTEM_PROMPT },
|
||||
{ role: 'user', content: input.prompt }
|
||||
@@ -423,22 +464,33 @@ export function useGroupReportGeneration({
|
||||
)
|
||||
)
|
||||
if (!result.success || !result.data) throw new Error(result.error || 'AI 请求失败')
|
||||
writeReportLog('info', '模型响应完成', {
|
||||
outputLength: result.data.length,
|
||||
usage: result.usage
|
||||
})
|
||||
|
||||
const tokenUsage =
|
||||
result.usage && result.usage.total
|
||||
? result.usage
|
||||
: estimateTokenUsage(aiMessages, result.data)
|
||||
|
||||
const report = parseGroupDailyReport(
|
||||
result.data,
|
||||
input.topSpeakers,
|
||||
input.activeTimeline,
|
||||
input.voiceLeaderboard || [],
|
||||
input.metadata,
|
||||
input.media
|
||||
)
|
||||
let report
|
||||
try {
|
||||
report = parseGroupDailyReport(
|
||||
result.data,
|
||||
input.topSpeakers,
|
||||
input.activeTimeline,
|
||||
input.voiceLeaderboard || [],
|
||||
input.metadata,
|
||||
input.media
|
||||
)
|
||||
} catch (parseError) {
|
||||
writeReportLog('error', '日报 JSON 解析失败', jsonErrorContext(result.data, parseError))
|
||||
throw parseError
|
||||
}
|
||||
|
||||
setPhase('exportingReport')
|
||||
failedAt = '导出 HTML 与 PNG'
|
||||
const exported = await withTimeout(
|
||||
window.api.exportGroupReport({ report, metadata: input.metadata, templateId }),
|
||||
'日报图片导出'
|
||||
@@ -474,8 +526,19 @@ export function useGroupReportGeneration({
|
||||
generationLogs: [...logs]
|
||||
})
|
||||
setPhase('success')
|
||||
writeReportLog('info', '群聊日报生成成功', {
|
||||
durationMs: Date.now() - startGenerateTime,
|
||||
htmlPath: exported.htmlPath,
|
||||
pngPath: exported.pngPath
|
||||
})
|
||||
} catch (generateError) {
|
||||
setError(errorMessage(generateError))
|
||||
const message = errorMessage(generateError)
|
||||
writeReportLog('error', '群聊日报生成失败', {
|
||||
error: message,
|
||||
failedAt,
|
||||
durationMs: Date.now() - startGenerateTime
|
||||
})
|
||||
setError(message)
|
||||
setPhase('error')
|
||||
}
|
||||
}, [
|
||||
@@ -485,6 +548,7 @@ export function useGroupReportGeneration({
|
||||
modelConfig,
|
||||
reportTimeoutSeconds,
|
||||
sourceContact,
|
||||
summaryDateRange,
|
||||
summaryMessageTypes,
|
||||
templateId
|
||||
])
|
||||
|
||||
@@ -4,6 +4,37 @@ import App from './App'
|
||||
import './styles/tokens.css'
|
||||
import './assets/main.css'
|
||||
|
||||
window.addEventListener('error', (event) => {
|
||||
void window.api
|
||||
.writeAppLog({
|
||||
level: 'error',
|
||||
scope: 'renderer',
|
||||
message: event.message || 'Renderer 未捕获错误',
|
||||
details: {
|
||||
filename: event.filename,
|
||||
line: event.lineno,
|
||||
column: event.colno,
|
||||
stack: event.error instanceof Error ? event.error.stack : undefined
|
||||
}
|
||||
})
|
||||
.catch(() => undefined)
|
||||
})
|
||||
|
||||
window.addEventListener('unhandledrejection', (event) => {
|
||||
const reason = event.reason
|
||||
void window.api
|
||||
.writeAppLog({
|
||||
level: 'error',
|
||||
scope: 'renderer',
|
||||
message: reason instanceof Error ? reason.message : 'Renderer Promise 未处理拒绝',
|
||||
details: {
|
||||
stack: reason instanceof Error ? reason.stack : undefined,
|
||||
reason: reason instanceof Error ? undefined : String(reason)
|
||||
}
|
||||
})
|
||||
.catch(() => undefined)
|
||||
})
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
|
||||
Reference in New Issue
Block a user