fix(logs): clarify error request logs list behavior

This commit is contained in:
Supra4E8C
2025-12-26 18:42:41 +08:00
parent ea1bdc3ac1
commit 312a06a8b8
3 changed files with 64 additions and 29 deletions

View File

@@ -589,6 +589,7 @@
"error_log_button": "Select Error Log",
"error_logs_modal_title": "Error Request Logs",
"error_logs_description": "Pick an error request log file to download (only generated when request logging is off).",
"error_logs_request_log_enabled": "Request logging is enabled, so this list will always be empty. Disable request logging and refresh to view error logs.",
"error_logs_empty": "No error request log files found",
"error_logs_load_error": "Failed to load error log list",
"error_logs_size": "Size",

View File

@@ -589,6 +589,7 @@
"error_log_button": "选择错误日志",
"error_logs_modal_title": "错误请求日志",
"error_logs_description": "请选择要下载的错误请求日志文件(仅在关闭请求日志时生成)。",
"error_logs_request_log_enabled": "当前已开启请求日志,按接口约定错误请求日志列表会始终为空。关闭请求日志后再刷新即可查看。",
"error_logs_empty": "暂无错误请求日志文件",
"error_logs_load_error": "加载错误日志列表失败",
"error_logs_size": "大小",

View File

@@ -14,7 +14,7 @@ import {
IconTrash2,
IconX,
} from '@/components/ui/icons';
import { useNotificationStore, useAuthStore } from '@/stores';
import { useAuthStore, useConfigStore, useNotificationStore } from '@/stores';
import { logsApi } from '@/services/api/logs';
import { MANAGEMENT_API_PREFIX } from '@/utils/constants';
import { formatUnixTimestamp } from '@/utils/format';
@@ -361,6 +361,7 @@ export function LogsPage() {
const { t } = useTranslation();
const { showNotification } = useNotificationStore();
const connectionStatus = useAuthStore((state) => state.connectionStatus);
const requestLogEnabled = useConfigStore((state) => state.config?.requestLog ?? false);
const [activeTab, setActiveTab] = useState<TabType>('logs');
const [logState, setLogState] = useState<LogState>({ buffer: [], visibleFrom: 0 });
@@ -372,6 +373,7 @@ export function LogsPage() {
const [hideManagementLogs, setHideManagementLogs] = useState(false);
const [errorLogs, setErrorLogs] = useState<ErrorLogItem[]>([]);
const [loadingErrors, setLoadingErrors] = useState(false);
const [errorLogsError, setErrorLogsError] = useState('');
const logViewerRef = useRef<HTMLDivElement | null>(null);
const pendingScrollToBottomRef = useRef(false);
@@ -488,14 +490,18 @@ export function LogsPage() {
}
setLoadingErrors(true);
setErrorLogsError('');
try {
const res = await logsApi.fetchErrorLogs();
// API 返回 { files: [...] }
setErrorLogs(Array.isArray(res.files) ? res.files : []);
} catch (err: unknown) {
console.error('Failed to load error logs:', err);
// 静默失败,不影响主日志显示
setErrorLogs([]);
const message = getErrorMessage(err);
setErrorLogsError(
message ? `${t('logs.error_logs_load_error')}: ${message}` : t('logs.error_logs_load_error')
);
} finally {
setLoadingErrors(false);
}
@@ -525,11 +531,17 @@ export function LogsPage() {
if (connectionStatus === 'connected') {
latestTimestampRef.current = 0;
loadLogs(false);
loadErrorLogs();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [connectionStatus]);
useEffect(() => {
if (activeTab !== 'errors') return;
if (connectionStatus !== 'connected') return;
void loadErrorLogs();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeTab, connectionStatus, requestLogEnabled]);
useEffect(() => {
if (!autoRefresh || connectionStatus !== 'connected') {
return;
@@ -877,38 +889,59 @@ export function LogsPage() {
{activeTab === 'errors' && (
<Card
extra={
<Button variant="secondary" size="sm" onClick={loadErrorLogs} loading={loadingErrors}>
<Button
variant="secondary"
size="sm"
onClick={loadErrorLogs}
loading={loadingErrors}
disabled={disableControls}
>
{t('common.refresh')}
</Button>
}
>
<div className={styles.errorPanel}>
{errorLogs.length === 0 ? (
<div className="hint">{t('logs.error_logs_empty')}</div>
) : (
<div className="item-list">
{errorLogs.map((item) => (
<div key={item.name} className="item-row">
<div className="item-meta">
<div className="item-title">{item.name}</div>
<div className="item-subtitle">
{item.size ? `${(item.size / 1024).toFixed(1)} KB` : ''}{' '}
{item.modified ? formatUnixTimestamp(item.modified) : ''}
</div>
</div>
<div className="item-actions">
<Button
variant="secondary"
size="sm"
onClick={() => downloadErrorLog(item.name)}
>
{t('logs.error_logs_download')}
</Button>
</div>
</div>
))}
<div className="stack">
<div className="hint">{t('logs.error_logs_description')}</div>
{requestLogEnabled && (
<div>
<div className="status-badge warning">{t('logs.error_logs_request_log_enabled')}</div>
</div>
)}
{errorLogsError && <div className="error-box">{errorLogsError}</div>}
<div className={styles.errorPanel}>
{loadingErrors ? (
<div className="hint">{t('common.loading')}</div>
) : errorLogs.length === 0 ? (
<div className="hint">{t('logs.error_logs_empty')}</div>
) : (
<div className="item-list">
{errorLogs.map((item) => (
<div key={item.name} className="item-row">
<div className="item-meta">
<div className="item-title">{item.name}</div>
<div className="item-subtitle">
{item.size ? `${(item.size / 1024).toFixed(1)} KB` : ''}{' '}
{item.modified ? formatUnixTimestamp(item.modified) : ''}
</div>
</div>
<div className="item-actions">
<Button
variant="secondary"
size="sm"
onClick={() => downloadErrorLog(item.name)}
disabled={disableControls}
>
{t('logs.error_logs_download')}
</Button>
</div>
</div>
))}
</div>
)}
</div>
</div>
</Card>
)}