Skip to content

Commit f2dee06

Browse files
committed
fix(logging): 完善服务端错误日志记录并支持设置页打开日志
- 为后端增加 5xx 请求与未处理异常日志记录 - 新增前端服务端错误上报工具与管理接口 - 在聊天导出、朋友圈信息加载、通用 API 请求中补充错误上报 - 设置页新增日志文件路径展示与一键打开能力 - 增加服务端错误日志相关测试
1 parent ecca6fe commit f2dee06

9 files changed

Lines changed: 710 additions & 9 deletions

File tree

frontend/components/SettingsDialog.vue

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,26 @@
167167
{{ desktopOutputDirError }}
168168
</div>
169169
</div>
170+
171+
<div class="px-3.5 py-3">
172+
<div class="flex flex-col gap-1.5 sm:flex-row sm:items-center sm:justify-between">
173+
<div class="min-w-0 flex-1">
174+
<div class="text-[13px] font-medium text-[#222]">日志文件</div>
175+
<div class="mt-0.5 text-[11px] text-[#909090] break-words">{{ desktopLogFileText }}</div>
176+
</div>
177+
<button
178+
type="button"
179+
class="shrink-0 rounded-[6px] border border-[#e2e2e2] bg-white px-2 py-1 text-[12px] text-[#222] transition hover:bg-[#f9f9f9] disabled:cursor-not-allowed disabled:opacity-50"
180+
:disabled="desktopLogFileLoading || desktopLogFileOpening"
181+
@click="onOpenBackendLogFile"
182+
>
183+
{{ desktopLogFileOpening ? '打开中...' : '打开日志' }}
184+
</button>
185+
</div>
186+
<div v-if="desktopLogFileError" class="mt-1.5 text-[11px] text-red-600 whitespace-pre-wrap">
187+
{{ desktopLogFileError }}
188+
</div>
189+
</div>
170190
</div>
171191
</section>
172192

@@ -271,8 +291,9 @@
271291
<script setup>
272292
import { DESKTOP_SETTING_AUTO_REALTIME_KEY, DESKTOP_SETTING_DEFAULT_TO_CHAT_KEY, SNS_SETTING_USE_CACHE_KEY, readLocalBoolSetting, writeLocalBoolSetting } from '~/utils/desktop-settings'
273293
import { readApiBaseOverride, writeApiBaseOverride } from '~/utils/api-settings'
294+
import { reportServerErrorFromError } from '~/utils/server-error-logging'
274295
275-
defineProps({
296+
const props = defineProps({
276297
open: {
277298
type: Boolean,
278299
default: false,
@@ -331,6 +352,15 @@ const desktopOutputDirText = computed(() => {
331352
return v || ''
332353
})
333354
355+
const desktopLogFilePath = ref('')
356+
const desktopLogFileLoading = ref(false)
357+
const desktopLogFileOpening = ref(false)
358+
const desktopLogFileError = ref('')
359+
const desktopLogFileText = computed(() => {
360+
const v = String(desktopLogFilePath.value || '').trim()
361+
return v || ''
362+
})
363+
334364
const switchTrackClass = (enabled, disabled = false) => {
335365
if (disabled) return enabled ? 'bg-[#07b75b] opacity-50 cursor-not-allowed' : 'bg-[#d0d0d0] opacity-50 cursor-not-allowed'
336366
return enabled ? 'bg-[#07b75b] hover:brightness-95' : 'bg-[#d0d0d0] hover:brightness-95'
@@ -376,6 +406,24 @@ const onEscKeydown = (event) => {
376406
handleClose()
377407
}
378408
409+
const fetchAdminEndpoint = async (url, options = {}) => {
410+
const apiBase = useApiBase()
411+
try {
412+
return await $fetch(url, {
413+
baseURL: apiBase,
414+
...options,
415+
})
416+
} catch (e) {
417+
await reportServerErrorFromError(e, {
418+
method: options?.method || 'GET',
419+
requestUrl: url,
420+
source: 'SettingsDialog',
421+
apiBase,
422+
})
423+
throw e
424+
}
425+
}
426+
379427
const refreshDesktopAutoLaunch = async () => {
380428
if (!process.client || typeof window === 'undefined') return
381429
if (!window.wechatDesktop?.getAutoLaunch) return
@@ -452,8 +500,7 @@ const refreshDesktopBackendPort = async () => {
452500
}
453501
454502
try {
455-
const apiBase = useApiBase()
456-
const resp = await $fetch('/admin/port', { baseURL: apiBase })
503+
const resp = await fetchAdminEndpoint('/admin/port')
457504
const n = Number(resp?.port)
458505
const d = Number(resp?.default_port)
459506
if (Number.isInteger(d) && d >= 1 && d <= 65535) desktopBackendPortDefault.value = d
@@ -510,6 +557,34 @@ const onDesktopOpenOutputDir = async () => {
510557
}
511558
}
512559
560+
const refreshBackendLogFileInfo = async () => {
561+
if (!process.client || typeof window === 'undefined') return
562+
desktopLogFileLoading.value = true
563+
desktopLogFileError.value = ''
564+
try {
565+
const resp = await fetchAdminEndpoint('/admin/log-file')
566+
desktopLogFilePath.value = String(resp?.path || '').trim()
567+
} catch (e) {
568+
desktopLogFileError.value = e?.message || '读取日志文件失败'
569+
} finally {
570+
desktopLogFileLoading.value = false
571+
}
572+
}
573+
574+
const onOpenBackendLogFile = async () => {
575+
if (!process.client || typeof window === 'undefined') return
576+
desktopLogFileOpening.value = true
577+
desktopLogFileError.value = ''
578+
try {
579+
const resp = await fetchAdminEndpoint('/admin/log-file/open', { method: 'POST' })
580+
if (resp?.path) desktopLogFilePath.value = String(resp.path || '').trim()
581+
} catch (e) {
582+
desktopLogFileError.value = e?.message || '打开日志文件失败'
583+
} finally {
584+
desktopLogFileOpening.value = false
585+
}
586+
}
587+
513588
const applyDesktopBackendPort = async () => {
514589
if (!process.client || typeof window === 'undefined') return
515590
const raw = String(desktopBackendPortInput.value || '').trim()
@@ -526,10 +601,9 @@ const applyDesktopBackendPort = async () => {
526601
return
527602
}
528603
529-
const currentApiBase = useApiBase()
530604
let currentBackendPort = null
531605
try {
532-
const info = await $fetch('/admin/port', { baseURL: currentApiBase })
606+
const info = await fetchAdminEndpoint('/admin/port')
533607
const p = Number(info?.port)
534608
if (Number.isInteger(p) && p >= 1 && p <= 65535) currentBackendPort = p
535609
} catch {}
@@ -540,8 +614,7 @@ const applyDesktopBackendPort = async () => {
540614
})()
541615
const isUiServedByBackend = !!(currentBackendPort && uiPort === currentBackendPort)
542616
543-
await $fetch('/admin/port', {
544-
baseURL: currentApiBase,
617+
await fetchAdminEndpoint('/admin/port', {
545618
method: 'POST',
546619
body: { port: n },
547620
})
@@ -624,6 +697,11 @@ const onDesktopCheckUpdates = async () => {
624697
await desktopUpdate.manualCheck()
625698
}
626699
700+
watch(() => props.open, async (isOpen) => {
701+
if (!isOpen) return
702+
await refreshBackendLogFileInfo()
703+
}, { immediate: true })
704+
627705
onMounted(async () => {
628706
if (process.client && typeof window !== 'undefined') {
629707
const isElectron = /electron/i.test(String(navigator.userAgent || ''))

frontend/composables/useApi.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { reportServerError } from '~/utils/server-error-logging'
2+
13
// API请求组合式函数
24
export const useApi = () => {
35
const baseURL = useApiBase()
@@ -8,10 +10,19 @@ export const useApi = () => {
810
const response = await $fetch(url, {
911
baseURL,
1012
...options,
11-
onResponseError({ response }) {
13+
async onResponseError({ response }) {
1214
if (response.status === 400) {
1315
throw new Error(response._data?.detail || '请求参数错误')
14-
} else if (response.status === 500) {
16+
} else if (response.status >= 500) {
17+
await reportServerError({
18+
status: response.status,
19+
method: options?.method || 'GET',
20+
requestUrl: url,
21+
message: '服务器错误,请稍后重试',
22+
backendDetail: response._data?.detail || '',
23+
source: 'useApi',
24+
apiBase: baseURL,
25+
})
1526
throw new Error('服务器错误,请稍后重试')
1627
}
1728
}

frontend/pages/chat/[[username]].vue

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2495,6 +2495,7 @@ definePageMeta({
24952495
import { useApi } from '~/composables/useApi'
24962496
import { parseTextWithEmoji } from '~/utils/wechat-emojis'
24972497
import { DESKTOP_SETTING_AUTO_REALTIME_KEY, readLocalBoolSetting } from '~/utils/desktop-settings'
2498+
import { reportServerErrorFromResponse } from '~/utils/server-error-logging'
24982499
import { heatColor } from '~/utils/wrapped/heatmap'
24992500
import { useChatAccountsStore } from '~/stores/chatAccounts'
25002501
import { useChatRealtimeStore } from '~/stores/chatRealtime'
@@ -3578,6 +3579,12 @@ const saveExportToSelectedFolder = async (options = {}) => {
35783579
try {
35793580
const resp = await fetch(getExportDownloadUrl(exportId))
35803581
if (!resp.ok) {
3582+
await reportServerErrorFromResponse(resp, {
3583+
method: 'GET',
3584+
requestUrl: getExportDownloadUrl(exportId),
3585+
message: `下载导出文件失败(${resp.status})`,
3586+
source: 'chat.exportDownload',
3587+
})
35813588
throw new Error(`下载导出文件失败(${resp.status})`)
35823589
}
35833590
const blob = await resp.blob()

frontend/pages/sns.vue

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,7 @@ import { useChatAccountsStore } from '~/stores/chatAccounts'
707707
import { usePrivacyStore } from '~/stores/privacy'
708708
import { parseTextWithEmoji } from '~/utils/wechat-emojis'
709709
import { SNS_SETTING_USE_CACHE_KEY, readLocalBoolSetting } from '~/utils/desktop-settings'
710+
import { reportServerErrorFromError } from '~/utils/server-error-logging'
710711

711712
useHead({ title: '朋友圈 - 微信数据分析助手' })
712713

@@ -1131,6 +1132,12 @@ const loadSelfInfo = async () => {
11311132
selfInfo.value = resp
11321133
}
11331134
} catch (e) {
1135+
await reportServerErrorFromError(e, {
1136+
method: 'GET',
1137+
requestUrl: `${apiBase}/sns/self_info?account=${encodeURIComponent(selectedAccount.value)}`,
1138+
source: 'sns.loadSelfInfo',
1139+
apiBase,
1140+
})
11341141
console.error('获取个人信息失败', e)
11351142
}
11361143
}

0 commit comments

Comments
 (0)