Change the log time to China
Build and Push CodeReview / build (push) Waiting to run Details

This commit is contained in:
vinland100 2026-01-09 13:23:49 +08:00
parent 5166027e2f
commit 3ce3767938
10 changed files with 86 additions and 39 deletions

View File

@ -0,0 +1,21 @@
from datetime import datetime, timedelta, timezone
# 中国标准时间 (UTC+8)
CHINA_TZ = timezone(timedelta(hours=8))
def get_now():
"""获取当前的中国时间 (UTC+8)"""
return datetime.now(CHINA_TZ)
def get_now_iso():
"""获取当前中国时间的 ISO 格式字符串"""
return get_now().isoformat()
def beijing_time(*args):
"""
用于 logging.Formatter.converter 的转换函数
"""
if args:
# args[0] 是 timestamp
return datetime.fromtimestamp(args[0], CHINA_TZ).timetuple()
return get_now().timetuple()

View File

@ -8,9 +8,12 @@ from app.api.v1.api import api_router
from app.db.session import AsyncSessionLocal from app.db.session import AsyncSessionLocal
from app.db.init_db import init_db from app.db.init_db import init_db
from app.core.timezone import beijing_time
# 配置日志 # 配置日志
logging.Formatter.converter = beijing_time
logging.basicConfig( logging.basicConfig(
level=logging.INFO, level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S' datefmt='%Y-%m-%d %H:%M:%S'
) )

View File

@ -7,7 +7,7 @@ import asyncio
import json import json
import logging import logging
from typing import Optional, Dict, Any, List, AsyncGenerator, Callable from typing import Optional, Dict, Any, List, AsyncGenerator, Callable
from datetime import datetime, timezone from app.core.timezone import get_now, get_now_iso
from dataclasses import dataclass from dataclasses import dataclass
import uuid import uuid
@ -281,7 +281,7 @@ class EventManager:
): ):
"""添加事件""" """添加事件"""
event_id = str(uuid.uuid4()) event_id = str(uuid.uuid4())
timestamp = datetime.now(timezone.utc) timestamp = get_now()
event_data = { event_data = {
"id": event_id, "id": event_id,
@ -379,6 +379,7 @@ class EventManager:
finding_id=event_data["finding_id"], finding_id=event_data["finding_id"],
tokens_used=event_data["tokens_used"], tokens_used=event_data["tokens_used"],
event_metadata=sanitize_dict(event_data["metadata"]), # 🔥 清理元数据 event_metadata=sanitize_dict(event_data["metadata"]), # 🔥 清理元数据
created_at=get_now(),
) )
db.add(event) db.add(event)
await db.commit() await db.commit()
@ -523,7 +524,7 @@ class EventManager:
except asyncio.TimeoutError: except asyncio.TimeoutError:
# 发送心跳 # 发送心跳
yield {"event_type": "heartbeat", "timestamp": datetime.now(timezone.utc).isoformat()} yield {"event_type": "heartbeat", "timestamp": get_now_iso()}
except GeneratorExit: except GeneratorExit:
# SSE 连接断开 # SSE 连接断开

View File

@ -8,7 +8,7 @@ import logging
from enum import Enum from enum import Enum
from typing import Any, Dict, Optional, AsyncGenerator, List from typing import Any, Dict, Optional, AsyncGenerator, List
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import datetime, timezone from app.core.timezone import get_now_iso
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -68,7 +68,7 @@ class StreamEvent:
"""流式事件""" """流式事件"""
event_type: StreamEventType event_type: StreamEventType
data: Dict[str, Any] = field(default_factory=dict) data: Dict[str, Any] = field(default_factory=dict)
timestamp: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat()) timestamp: str = field(default_factory=get_now_iso)
sequence: int = 0 sequence: int = 0
# 可选字段 # 可选字段

View File

@ -12,7 +12,7 @@ Tracer - 审计追踪器
import csv import csv
import json import json
import logging import logging
from datetime import datetime, timezone from app.core.timezone import get_now, get_now_iso
from pathlib import Path from pathlib import Path
from typing import Any, Callable, Dict, List, Optional from typing import Any, Callable, Dict, List, Optional
from uuid import uuid4 from uuid import uuid4
@ -53,7 +53,7 @@ class Tracer:
# 运行标识 # 运行标识
self.run_name = run_name self.run_name = run_name
self.run_id = run_name or f"run-{uuid4().hex[:8]}" self.run_id = run_name or f"run-{uuid4().hex[:8]}"
self.start_time = datetime.now(timezone.utc).isoformat() self.start_time = get_now_iso()
self.end_time: Optional[str] = None self.end_time: Optional[str] = None
# 追踪数据 # 追踪数据
@ -148,8 +148,8 @@ class Tracer:
"type": agent_type, "type": agent_type,
"status": "running", "status": "running",
"parent_id": parent_id, "parent_id": parent_id,
"created_at": datetime.now(timezone.utc).isoformat(), "created_at": get_now_iso(),
"updated_at": datetime.now(timezone.utc).isoformat(), "updated_at": get_now_iso(),
"tool_executions": [], "tool_executions": [],
"findings_count": 0, "findings_count": 0,
} }
@ -166,13 +166,13 @@ class Tracer:
"""更新 Agent 状态""" """更新 Agent 状态"""
if agent_id in self.agents: if agent_id in self.agents:
self.agents[agent_id]["status"] = status self.agents[agent_id]["status"] = status
self.agents[agent_id]["updated_at"] = datetime.now(timezone.utc).isoformat() self.agents[agent_id]["updated_at"] = get_now_iso()
if error_message: if error_message:
self.agents[agent_id]["error_message"] = error_message self.agents[agent_id]["error_message"] = error_message
if status in ["completed", "failed", "stopped"]: if status in ["completed", "failed", "stopped"]:
self.agents[agent_id]["finished_at"] = datetime.now(timezone.utc).isoformat() self.agents[agent_id]["finished_at"] = get_now_iso()
# 触发回调 # 触发回调
if self.agent_status_callback: if self.agent_status_callback:
@ -193,7 +193,7 @@ class Tracer:
execution_id = self._next_execution_id execution_id = self._next_execution_id
self._next_execution_id += 1 self._next_execution_id += 1
now = datetime.now(timezone.utc).isoformat() now = get_now_iso()
# 清理过大的参数 # 清理过大的参数
cleaned_args = self._clean_args(args) cleaned_args = self._clean_args(args)
@ -226,7 +226,7 @@ class Tracer:
"""更新工具执行状态""" """更新工具执行状态"""
if execution_id in self.tool_executions: if execution_id in self.tool_executions:
self.tool_executions[execution_id]["status"] = status self.tool_executions[execution_id]["status"] = status
self.tool_executions[execution_id]["completed_at"] = datetime.now(timezone.utc).isoformat() self.tool_executions[execution_id]["completed_at"] = get_now_iso()
# 清理过大的结果 # 清理过大的结果
if result is not None: if result is not None:
@ -295,7 +295,7 @@ class Tracer:
"content": content, "content": content,
"role": role, "role": role,
"agent_id": agent_id, "agent_id": agent_id,
"timestamp": datetime.now(timezone.utc).isoformat(), "timestamp": get_now_iso(),
"metadata": metadata or {}, "metadata": metadata or {},
} }
@ -324,7 +324,7 @@ class Tracer:
"vulnerability_type": vulnerability_type, "vulnerability_type": vulnerability_type,
"file_path": file_path, "file_path": file_path,
"agent_id": agent_id, "agent_id": agent_id,
"timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC"), "timestamp": get_now().strftime("%Y-%m-%d %H:%M:%S") + " CST",
} }
self.vulnerability_reports.append(report) self.vulnerability_reports.append(report)
@ -365,12 +365,12 @@ class Tracer:
"scan_completed": True, "scan_completed": True,
"content": content, "content": content,
"success": success, "success": success,
"completed_at": datetime.now(timezone.utc).isoformat(), "completed_at": get_now_iso(),
"total_vulnerabilities": len(self.vulnerability_reports), "total_vulnerabilities": len(self.vulnerability_reports),
} }
self.run_metadata["status"] = "completed" if success else "failed" self.run_metadata["status"] = "completed" if success else "failed"
self.end_time = datetime.now(timezone.utc).isoformat() self.end_time = get_now_iso()
self.run_metadata["end_time"] = self.end_time self.run_metadata["end_time"] = self.end_time
logger.info(f"Tracer: Final scan result set, success={success}") logger.info(f"Tracer: Final scan result set, success={success}")
@ -386,7 +386,7 @@ class Tracer:
run_dir = self.get_run_dir() run_dir = self.get_run_dir()
if mark_complete: if mark_complete:
self.end_time = datetime.now(timezone.utc).isoformat() self.end_time = get_now_iso()
self.run_metadata["end_time"] = self.end_time self.run_metadata["end_time"] = self.end_time
# 保存最终报告 # 保存最终报告
@ -410,7 +410,7 @@ class Tracer:
with report_file.open("w", encoding="utf-8") as f: with report_file.open("w", encoding="utf-8") as f:
f.write("# 安全审计报告\n\n") f.write("# 安全审计报告\n\n")
f.write(f"**生成时间:** {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC')}\n") f.write(f"**生成时间:** {get_now().strftime('%Y-%m-%d %H:%M:%S')} CST\n")
f.write(f"**运行ID:** {self.run_id}\n\n") f.write(f"**运行ID:** {self.run_id}\n\n")
# 统计信息 # 统计信息
@ -518,11 +518,11 @@ class Tracer:
def _calculate_duration(self) -> float: def _calculate_duration(self) -> float:
"""计算运行时长""" """计算运行时长"""
try: try:
start = datetime.fromisoformat(self.start_time.replace("Z", "+00:00")) start = datetime.fromisoformat(self.start_time)
if self.end_time: if self.end_time:
end = datetime.fromisoformat(self.end_time.replace("Z", "+00:00")) end = datetime.fromisoformat(self.end_time)
else: else:
end = datetime.now(timezone.utc) end = get_now()
return (end - start).total_seconds() return (end - start).total_seconds()
except (ValueError, TypeError): except (ValueError, TypeError):
return 0.0 return 0.0

View File

@ -5,6 +5,7 @@ PDF 报告生成服务 - 专业审计版 (WeasyPrint)
import io import io
import html import html
from datetime import datetime from datetime import datetime
from app.core.timezone import get_now
from typing import List, Dict, Any from typing import List, Dict, Any
import math import math
import os import os
@ -475,8 +476,8 @@ class ReportGenerator:
context = { context = {
'title': '代码审计报告', 'title': '代码审计报告',
'subtitle': f'即时分析 | 语言: {language.capitalize()}', 'subtitle': f'即时分析 | 语言: {language.capitalize()}',
'generated_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'generated_at': get_now().strftime('%Y-%m-%d %H:%M:%S'),
'report_id': f"INST-{int(datetime.now().timestamp())}", 'report_id': f"INST-{int(get_now().timestamp())}",
'score': score, 'score': score,
'stats': [ 'stats': [
('问题总数', len(issues)), ('问题总数', len(issues)),
@ -493,7 +494,7 @@ class ReportGenerator:
context = { context = {
'title': '项目代码审计报告', 'title': '项目代码审计报告',
'subtitle': f"项目: {project} | 分支: {task.get('branch_name', 'default')}", 'subtitle': f"项目: {project} | 分支: {task.get('branch_name', 'default')}",
'generated_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'generated_at': get_now().strftime('%Y-%m-%d %H:%M:%S'),
'report_id': f"TASK-{task.get('id', '')[:8]}", 'report_id': f"TASK-{task.get('id', '')[:8]}",
'score': score, 'score': score,
'stats': [ 'stats': [

View File

@ -59,10 +59,7 @@ function agentAuditReducer(state: AgentAuditState, action: AgentAuditAction): Ag
return { ...state, logs: action.payload }; return { ...state, logs: action.payload };
case 'ADD_LOG': { case 'ADD_LOG': {
const { id: providedId, ...logData } = action.payload; const newLog = createLogItem(action.payload);
const newLog = providedId
? { ...createLogItem(logData), id: providedId }
: createLogItem(logData);
return { ...state, logs: [...state.logs, newLog] }; return { ...state, logs: [...state.logs, newLog] };
} }
@ -194,11 +191,11 @@ export function useAgentAuditState() {
dispatch({ type: 'SET_AGENT_TREE', payload: tree }); dispatch({ type: 'SET_AGENT_TREE', payload: tree });
}, []); }, []);
const addLog = useCallback((log: Omit<LogItem, 'id' | 'time'>): string => { const addLog = useCallback((log: Omit<LogItem, 'id' | 'time'> & { id?: string; time?: string }): string => {
const newLog = createLogItem(log); const newLog = createLogItem(log);
dispatch({ type: 'SET_LOGS', payload: [...state.logs, newLog] }); dispatch({ type: 'ADD_LOG', payload: newLog });
return newLog.id; return newLog.id;
}, [state.logs]); }, []);
const updateLog = useCallback((id: string, updates: Partial<LogItem>) => { const updateLog = useCallback((id: string, updates: Partial<LogItem>) => {
dispatch({ type: 'UPDATE_LOG', payload: { id, updates } }); dispatch({ type: 'UPDATE_LOG', payload: { id, updates } });

View File

@ -199,6 +199,7 @@ function AgentAuditPageContent() {
title: event.message?.slice(0, 100) + (event.message && event.message.length > 100 ? '...' : '') || 'Thinking...', title: event.message?.slice(0, 100) + (event.message && event.message.length > 100 ? '...' : '') || 'Thinking...',
content: event.message || (event.metadata?.thought as string) || '', content: event.message || (event.metadata?.thought as string) || '',
agentName, agentName,
time: event.timestamp,
} }
}); });
processedCount++; processedCount++;
@ -217,6 +218,7 @@ function AgentAuditPageContent() {
status: 'running' as const, status: 'running' as const,
}, },
agentName, agentName,
time: event.timestamp,
} }
}); });
processedCount++; processedCount++;
@ -237,6 +239,7 @@ function AgentAuditPageContent() {
status: 'completed' as const, status: 'completed' as const,
}, },
agentName, agentName,
time: event.timestamp,
} }
}); });
processedCount++; processedCount++;
@ -253,6 +256,7 @@ function AgentAuditPageContent() {
title: event.message || (event.metadata?.title as string) || 'Vulnerability found', title: event.message || (event.metadata?.title as string) || 'Vulnerability found',
severity: (event.metadata?.severity as string) || 'medium', severity: (event.metadata?.severity as string) || 'medium',
agentName, agentName,
time: event.timestamp,
} }
}); });
processedCount++; processedCount++;
@ -271,6 +275,7 @@ function AgentAuditPageContent() {
type: 'dispatch', type: 'dispatch',
title: event.message || `Event: ${event.event_type}`, title: event.message || `Event: ${event.event_type}`,
agentName, agentName,
time: event.timestamp,
} }
}); });
processedCount++; processedCount++;
@ -284,6 +289,7 @@ function AgentAuditPageContent() {
type: 'info', type: 'info',
title: event.message || 'Task completed', title: event.message || 'Task completed',
agentName, agentName,
time: event.timestamp,
} }
}); });
processedCount++; processedCount++;
@ -297,6 +303,7 @@ function AgentAuditPageContent() {
type: 'error', type: 'error',
title: event.message || 'Task error', title: event.message || 'Task error',
agentName, agentName,
time: event.timestamp,
} }
}); });
processedCount++; processedCount++;
@ -310,6 +317,7 @@ function AgentAuditPageContent() {
type: 'info', type: 'info',
title: event.message || 'Task cancelled', title: event.message || 'Task cancelled',
agentName, agentName,
time: event.timestamp,
} }
}); });
processedCount++; processedCount++;
@ -335,6 +343,7 @@ function AgentAuditPageContent() {
progressKey: matchedProgress.key, progressKey: matchedProgress.key,
title: event.message, title: event.message,
agentName, agentName,
time: event.timestamp,
} }
}); });
} else { } else {
@ -344,6 +353,7 @@ function AgentAuditPageContent() {
type: 'info', type: 'info',
title: event.message, title: event.message,
agentName, agentName,
time: event.timestamp,
} }
}); });
} }
@ -374,6 +384,7 @@ function AgentAuditPageContent() {
progressKey: matchedProgress.key, progressKey: matchedProgress.key,
title: message, title: message,
agentName, agentName,
time: event.timestamp,
} }
}); });
} else { } else {
@ -383,6 +394,7 @@ function AgentAuditPageContent() {
type: event.event_type === 'error' ? 'error' : 'info', type: event.event_type === 'error' ? 'error' : 'info',
title: message, title: message,
agentName, agentName,
time: event.timestamp,
} }
}); });
} }
@ -406,6 +418,7 @@ function AgentAuditPageContent() {
type: 'info', type: 'info',
title: event.message, title: event.message,
agentName, agentName,
time: event.timestamp,
} }
}); });
processedCount++; processedCount++;

View File

@ -76,7 +76,7 @@ export type AgentAuditAction =
| { type: 'ADD_FINDING'; payload: Partial<AgentFinding> & { id: string } } | { type: 'ADD_FINDING'; payload: Partial<AgentFinding> & { id: string } }
| { type: 'SET_AGENT_TREE'; payload: AgentTreeResponse } | { type: 'SET_AGENT_TREE'; payload: AgentTreeResponse }
| { type: 'SET_LOGS'; payload: LogItem[] } | { type: 'SET_LOGS'; payload: LogItem[] }
| { type: 'ADD_LOG'; payload: Omit<LogItem, 'id' | 'time'> & { id?: string } } | { type: 'ADD_LOG'; payload: Omit<LogItem, 'id' | 'time'> & { id?: string; time?: string } }
| { type: 'UPDATE_LOG'; payload: { id: string; updates: Partial<LogItem> } } | { type: 'UPDATE_LOG'; payload: { id: string; updates: Partial<LogItem> } }
| { type: 'UPDATE_OR_ADD_PROGRESS_LOG'; payload: { progressKey: string; title: string; agentName?: string } } | { type: 'UPDATE_OR_ADD_PROGRESS_LOG'; payload: { progressKey: string; title: string; agentName?: string } }
| { type: 'COMPLETE_TOOL_LOG'; payload: { toolName: string; output: string; duration: number } } | { type: 'COMPLETE_TOOL_LOG'; payload: { toolName: string; output: string; duration: number } }

View File

@ -73,7 +73,18 @@ export function resetLogIdCounter(): void {
* Get current time string for logs * Get current time string for logs
*/ */
export function getTimeString(): string { export function getTimeString(): string {
return new Date().toLocaleTimeString('en-US', { return formatTimestamp(new Date());
}
/**
* Format timestamp (date or string) to HH:mm:ss
*/
export function formatTimestamp(time?: string | Date | number): string {
if (!time) return getTimeString();
const date = new Date(time);
if (isNaN(date.getTime())) return getTimeString();
return date.toLocaleTimeString('en-US', {
hour12: false, hour12: false,
hour: '2-digit', hour: '2-digit',
minute: '2-digit', minute: '2-digit',
@ -84,12 +95,12 @@ export function getTimeString(): string {
/** /**
* Create a log item * Create a log item
*/ */
export function createLogItem(item: Omit<LogItem, 'id' | 'time'>): LogItem { export function createLogItem(item: Omit<LogItem, 'id' | 'time'> & { id?: string; time?: string }): LogItem {
return { return {
...item, ...item,
id: generateLogId(), id: item.id || generateLogId(),
time: getTimeString(), time: item.time ? formatTimestamp(item.time) : getTimeString(),
}; } as LogItem;
} }
/** /**