feat(logging-p0): add useLogger to renderer critical path components
Replace console.error with structured useLogger calls in 5 key renderer files (Cleaner, LoginDialog, Extractor, OperationHistory, MaterialType) to enable persistent log capture for frontend error diagnosis. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
|
||||
import React, { useState, useEffect, useCallback } from 'react'
|
||||
import { Modal } from './ui/Modal'
|
||||
import { useLogger } from '../hooks/useLogger'
|
||||
import {
|
||||
RefreshCw,
|
||||
Trash2,
|
||||
@@ -82,6 +83,7 @@ export const ExtractorOperationHistoryModal: React.FC<ExtractorOperationHistoryM
|
||||
const [deleting, setDeleting] = useState<Set<string>>(new Set())
|
||||
const [allUsers, setAllUsers] = useState<string[]>([])
|
||||
const [selectedUsers, setSelectedUsers] = useState<string[]>([])
|
||||
const logger = useLogger('OperationHistory')
|
||||
|
||||
const isAdmin = user?.userType === 'Admin'
|
||||
|
||||
@@ -116,9 +118,11 @@ export const ExtractorOperationHistoryModal: React.FC<ExtractorOperationHistoryM
|
||||
setAllUsers(usernames)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch users:', err)
|
||||
logger.error('Failed to fetch users list', {
|
||||
error: err instanceof Error ? err.message : String(err)
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
}, [logger])
|
||||
|
||||
const fetchBatchDetails = useCallback(
|
||||
async (batchId: string) => {
|
||||
@@ -133,10 +137,13 @@ export const ExtractorOperationHistoryModal: React.FC<ExtractorOperationHistoryM
|
||||
setBatchDetails((prev) => new Map(prev).set(batchId, result.data!))
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch batch details:', err)
|
||||
logger.error('Failed to fetch batch details', {
|
||||
error: err instanceof Error ? err.message : String(err),
|
||||
batchId
|
||||
})
|
||||
}
|
||||
},
|
||||
[batchDetails]
|
||||
[batchDetails, logger]
|
||||
)
|
||||
|
||||
// Fetch batches when modal opens
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
import React, { useState, useRef } from 'react'
|
||||
import { Modal } from './ui/Modal'
|
||||
import { useLogger } from '../hooks/useLogger'
|
||||
|
||||
interface LoginDialogProps {
|
||||
isOpen: boolean
|
||||
@@ -31,6 +32,7 @@ export const LoginDialog: React.FC<LoginDialogProps> = ({
|
||||
const [errorMessage, setErrorMessage] = useState('')
|
||||
const usernameInputRef = useRef<HTMLInputElement>(null)
|
||||
const errorRef = useRef<HTMLDivElement>(null)
|
||||
const logger = useLogger('LoginDialog')
|
||||
|
||||
// Display error message with aria-live
|
||||
const showError = (message: string): void => {
|
||||
@@ -42,12 +44,14 @@ export const LoginDialog: React.FC<LoginDialogProps> = ({
|
||||
setErrorMessage('')
|
||||
|
||||
if (!username.trim()) {
|
||||
logger.warn('Login validation: empty username')
|
||||
showError('请输入用户名')
|
||||
usernameInputRef.current?.focus()
|
||||
return
|
||||
}
|
||||
|
||||
if (!password.trim()) {
|
||||
logger.warn('Login validation: empty password')
|
||||
showError('请输入密码')
|
||||
return
|
||||
}
|
||||
@@ -57,8 +61,8 @@ export const LoginDialog: React.FC<LoginDialogProps> = ({
|
||||
setIsLoggingIn(false)
|
||||
|
||||
if (!success) {
|
||||
logger.error('Login failed: invalid credentials', { username: username.trim(), computerName })
|
||||
showError('用户名或密码错误')
|
||||
setPassword('')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import { Modal } from './ui/Modal'
|
||||
import { showSuccess, showError, showInfo } from '../stores/useAppStore'
|
||||
import { ConfirmDialog } from './ui/ConfirmDialog'
|
||||
import { useConfirmDialog } from './ui/useConfirmDialog'
|
||||
import { useLogger } from '../hooks/useLogger'
|
||||
|
||||
interface MaterialTypeRecord {
|
||||
id?: number
|
||||
@@ -48,6 +49,7 @@ export const MaterialTypeManagementDialog: React.FC<MaterialTypeManagementDialog
|
||||
const [editingCell, setEditingCell] = useState<{ rowIndex: number; field: string } | null>(null)
|
||||
const [editValue, setEditValue] = useState('')
|
||||
const [selectedRowIndex, setSelectedRowIndex] = useState<number | null>(null)
|
||||
const logger = useLogger('MaterialType')
|
||||
|
||||
const tableRef = useRef<HTMLTableElement>(null)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
@@ -96,11 +98,15 @@ export const MaterialTypeManagementDialog: React.FC<MaterialTypeManagementDialog
|
||||
)
|
||||
setSelectedRowIndex(null)
|
||||
} catch (error) {
|
||||
console.error('Failed to load material types:', error)
|
||||
logger.error('Failed to load material types', {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
isAdmin,
|
||||
currentUsername
|
||||
})
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [currentUsername, isAdmin])
|
||||
}, [currentUsername, isAdmin, logger])
|
||||
|
||||
// Load data when dialog opens
|
||||
useEffect(() => {
|
||||
@@ -286,6 +292,12 @@ export const MaterialTypeManagementDialog: React.FC<MaterialTypeManagementDialog
|
||||
}
|
||||
} catch (error) {
|
||||
showError(`保存失败:${error instanceof Error ? error.message : '未知错误'}`)
|
||||
logger.error('Failed to save material types', {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
inserts: toInsert.length,
|
||||
updates: toUpdate.length,
|
||||
deletes: toDelete.length
|
||||
})
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect, useMemo, useRef, useCallback } from 'react'
|
||||
import { showSuccess, showError, showWarning, formatListMessage } from '../stores/useAppStore'
|
||||
import { useLogger } from './useLogger'
|
||||
import { ConfirmDialogProps } from '../components/ui/ConfirmDialog'
|
||||
import {
|
||||
buildDeletionPlan,
|
||||
@@ -20,6 +21,8 @@ import {
|
||||
import type { CleanerProgress, CleanerReportData, ValidationResult } from './cleaner/types'
|
||||
|
||||
export function useCleaner() {
|
||||
const logger = useLogger('Cleaner')
|
||||
|
||||
// Authentication & permissions
|
||||
const [isAdmin, setIsAdmin] = useState(false)
|
||||
const [currentUsername, setCurrentUsername] = useState<string>('')
|
||||
@@ -108,11 +111,13 @@ export function useCleaner() {
|
||||
setSelectedManagers(new Set([result.currentUsername]))
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Initialization failed:', err)
|
||||
logger.error('Cleaner page initialization failed', {
|
||||
error: err instanceof Error ? err.message : String(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
initializePage()
|
||||
}, [])
|
||||
}, [logger])
|
||||
|
||||
// Subscribe to cleaner progress events
|
||||
useEffect(() => {
|
||||
@@ -135,11 +140,13 @@ export function useCleaner() {
|
||||
setProcessConcurrency(result.processConcurrency)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load cleaner config:', err)
|
||||
logger.error('Failed to load cleaner config', {
|
||||
error: err instanceof Error ? err.message : String(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
loadCleanerConfig()
|
||||
}, [])
|
||||
}, [logger])
|
||||
|
||||
useEffect(() => {
|
||||
sessionStorage.setItem('cleaner_dryRun', dryRun.toString())
|
||||
@@ -155,7 +162,10 @@ export function useCleaner() {
|
||||
try {
|
||||
await window.electron.config.updateCleaner({ processConcurrency: clamped })
|
||||
} catch (err) {
|
||||
console.error('Failed to update cleaner config:', err)
|
||||
logger.error('Failed to update process concurrency', {
|
||||
error: err instanceof Error ? err.message : String(err),
|
||||
value: clamped
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { useEffect } from 'react'
|
||||
import type { LogLevel } from '../stores/extractorStore'
|
||||
import { useExtractorStore } from '../stores/extractorStore'
|
||||
import { useLogger } from './useLogger'
|
||||
|
||||
function isLogLevel(value: string): value is LogLevel {
|
||||
return ['info', 'success', 'warning', 'error', 'system'].includes(value)
|
||||
}
|
||||
|
||||
export function useExtractor() {
|
||||
const logger = useLogger('Extractor')
|
||||
|
||||
const {
|
||||
isRunning,
|
||||
isComplete,
|
||||
@@ -73,6 +76,11 @@ export function useExtractor() {
|
||||
'success',
|
||||
`提取完成:下载 ${data.downloadedFiles.length} 个文件,共 ${data.recordCount} 条记录`
|
||||
)
|
||||
logger.info('Extraction completed', {
|
||||
downloadedFiles: data.downloadedFiles.length,
|
||||
recordCount: data.recordCount,
|
||||
errorCount: data.errors.length
|
||||
})
|
||||
if (data.errors.length > 0) {
|
||||
addLog('warning', `存在 ${data.errors.length} 个错误`)
|
||||
// Log each error detail for debugging
|
||||
@@ -83,11 +91,13 @@ export function useExtractor() {
|
||||
} else {
|
||||
setError(response.error || '提取失败')
|
||||
addLog('error', response.error || '提取失败')
|
||||
logger.error('Extraction failed', { error: response.error })
|
||||
}
|
||||
} catch (err) {
|
||||
const errMsg = err instanceof Error ? err.message : '发生未知错误'
|
||||
setError(errMsg)
|
||||
addLog('error', errMsg)
|
||||
logger.error('Extraction exception', { error: errMsg })
|
||||
} finally {
|
||||
setRunning(false)
|
||||
setProgress(null)
|
||||
|
||||
Reference in New Issue
Block a user