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.init_db import init_db
from app.core.timezone import beijing_time
# 配置日志
logging.Formatter.converter = beijing_time
logging.basicConfig(
level=logging.INFO,
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)

View File

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

View File

@ -8,7 +8,7 @@ import logging
from enum import Enum
from typing import Any, Dict, Optional, AsyncGenerator, List
from dataclasses import dataclass, field
from datetime import datetime, timezone
from app.core.timezone import get_now_iso
logger = logging.getLogger(__name__)
@ -68,7 +68,7 @@ class StreamEvent:
"""流式事件"""
event_type: StreamEventType
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
# 可选字段

View File

@ -12,7 +12,7 @@ Tracer - 审计追踪器
import csv
import json
import logging
from datetime import datetime, timezone
from app.core.timezone import get_now, get_now_iso
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional
from uuid import uuid4
@ -53,7 +53,7 @@ class Tracer:
# 运行标识
self.run_name = run_name
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
# 追踪数据
@ -148,8 +148,8 @@ class Tracer:
"type": agent_type,
"status": "running",
"parent_id": parent_id,
"created_at": datetime.now(timezone.utc).isoformat(),
"updated_at": datetime.now(timezone.utc).isoformat(),
"created_at": get_now_iso(),
"updated_at": get_now_iso(),
"tool_executions": [],
"findings_count": 0,
}
@ -166,13 +166,13 @@ class Tracer:
"""更新 Agent 状态"""
if agent_id in self.agents:
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:
self.agents[agent_id]["error_message"] = error_message
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:
@ -193,7 +193,7 @@ class Tracer:
execution_id = self._next_execution_id
self._next_execution_id += 1
now = datetime.now(timezone.utc).isoformat()
now = get_now_iso()
# 清理过大的参数
cleaned_args = self._clean_args(args)
@ -226,7 +226,7 @@ class Tracer:
"""更新工具执行状态"""
if execution_id in self.tool_executions:
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:
@ -295,7 +295,7 @@ class Tracer:
"content": content,
"role": role,
"agent_id": agent_id,
"timestamp": datetime.now(timezone.utc).isoformat(),
"timestamp": get_now_iso(),
"metadata": metadata or {},
}
@ -324,7 +324,7 @@ class Tracer:
"vulnerability_type": vulnerability_type,
"file_path": file_path,
"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)
@ -365,12 +365,12 @@ class Tracer:
"scan_completed": True,
"content": content,
"success": success,
"completed_at": datetime.now(timezone.utc).isoformat(),
"completed_at": get_now_iso(),
"total_vulnerabilities": len(self.vulnerability_reports),
}
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
logger.info(f"Tracer: Final scan result set, success={success}")
@ -386,7 +386,7 @@ class Tracer:
run_dir = self.get_run_dir()
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
# 保存最终报告
@ -410,7 +410,7 @@ class Tracer:
with report_file.open("w", encoding="utf-8") as f:
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")
# 统计信息
@ -518,11 +518,11 @@ class Tracer:
def _calculate_duration(self) -> float:
"""计算运行时长"""
try:
start = datetime.fromisoformat(self.start_time.replace("Z", "+00:00"))
start = datetime.fromisoformat(self.start_time)
if self.end_time:
end = datetime.fromisoformat(self.end_time.replace("Z", "+00:00"))
end = datetime.fromisoformat(self.end_time)
else:
end = datetime.now(timezone.utc)
end = get_now()
return (end - start).total_seconds()
except (ValueError, TypeError):
return 0.0

View File

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

View File

@ -59,10 +59,7 @@ function agentAuditReducer(state: AgentAuditState, action: AgentAuditAction): Ag
return { ...state, logs: action.payload };
case 'ADD_LOG': {
const { id: providedId, ...logData } = action.payload;
const newLog = providedId
? { ...createLogItem(logData), id: providedId }
: createLogItem(logData);
const newLog = createLogItem(action.payload);
return { ...state, logs: [...state.logs, newLog] };
}
@ -194,11 +191,11 @@ export function useAgentAuditState() {
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);
dispatch({ type: 'SET_LOGS', payload: [...state.logs, newLog] });
dispatch({ type: 'ADD_LOG', payload: newLog });
return newLog.id;
}, [state.logs]);
}, []);
const updateLog = useCallback((id: string, updates: Partial<LogItem>) => {
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...',
content: event.message || (event.metadata?.thought as string) || '',
agentName,
time: event.timestamp,
}
});
processedCount++;
@ -217,6 +218,7 @@ function AgentAuditPageContent() {
status: 'running' as const,
},
agentName,
time: event.timestamp,
}
});
processedCount++;
@ -237,6 +239,7 @@ function AgentAuditPageContent() {
status: 'completed' as const,
},
agentName,
time: event.timestamp,
}
});
processedCount++;
@ -253,6 +256,7 @@ function AgentAuditPageContent() {
title: event.message || (event.metadata?.title as string) || 'Vulnerability found',
severity: (event.metadata?.severity as string) || 'medium',
agentName,
time: event.timestamp,
}
});
processedCount++;
@ -271,6 +275,7 @@ function AgentAuditPageContent() {
type: 'dispatch',
title: event.message || `Event: ${event.event_type}`,
agentName,
time: event.timestamp,
}
});
processedCount++;
@ -284,6 +289,7 @@ function AgentAuditPageContent() {
type: 'info',
title: event.message || 'Task completed',
agentName,
time: event.timestamp,
}
});
processedCount++;
@ -297,6 +303,7 @@ function AgentAuditPageContent() {
type: 'error',
title: event.message || 'Task error',
agentName,
time: event.timestamp,
}
});
processedCount++;
@ -310,6 +317,7 @@ function AgentAuditPageContent() {
type: 'info',
title: event.message || 'Task cancelled',
agentName,
time: event.timestamp,
}
});
processedCount++;
@ -335,6 +343,7 @@ function AgentAuditPageContent() {
progressKey: matchedProgress.key,
title: event.message,
agentName,
time: event.timestamp,
}
});
} else {
@ -344,6 +353,7 @@ function AgentAuditPageContent() {
type: 'info',
title: event.message,
agentName,
time: event.timestamp,
}
});
}
@ -374,6 +384,7 @@ function AgentAuditPageContent() {
progressKey: matchedProgress.key,
title: message,
agentName,
time: event.timestamp,
}
});
} else {
@ -383,6 +394,7 @@ function AgentAuditPageContent() {
type: event.event_type === 'error' ? 'error' : 'info',
title: message,
agentName,
time: event.timestamp,
}
});
}
@ -406,6 +418,7 @@ function AgentAuditPageContent() {
type: 'info',
title: event.message,
agentName,
time: event.timestamp,
}
});
processedCount++;

View File

@ -76,7 +76,7 @@ export type AgentAuditAction =
| { type: 'ADD_FINDING'; payload: Partial<AgentFinding> & { id: string } }
| { type: 'SET_AGENT_TREE'; payload: AgentTreeResponse }
| { 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_OR_ADD_PROGRESS_LOG'; payload: { progressKey: string; title: string; agentName?: string } }
| { 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
*/
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,
hour: '2-digit',
minute: '2-digit',
@ -84,12 +95,12 @@ export function getTimeString(): string {
/**
* 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 {
...item,
id: generateLogId(),
time: getTimeString(),
};
id: item.id || generateLogId(),
time: item.time ? formatTimestamp(item.time) : getTimeString(),
} as LogItem;
}
/**