a
This commit is contained in:
parent
79526151f3
commit
59b8e7b44f
|
|
@ -114,6 +114,16 @@ def setup_logging():
|
|||
# 让 handler 自己按组筛选(error/warning/info),这里只需要放宽到 INFO
|
||||
redis_handler.setLevel(logging.INFO)
|
||||
root_logger.addHandler(redis_handler)
|
||||
|
||||
# 诊断:启动时快速检测一次 Redis 可用性(失败不影响运行)
|
||||
try:
|
||||
client = redis_handler._get_redis() # noqa: SLF001(仅用于诊断)
|
||||
if client is None:
|
||||
logging.getLogger(__name__).warning(f"⚠ Redis 日志写入未启用。REDIS_URL={redis_url}")
|
||||
else:
|
||||
logging.getLogger(__name__).info(f"✓ Redis 日志写入已启用。REDIS_URL={redis_url}")
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -125,6 +125,76 @@ class LogsConfigUpdate(BaseModel):
|
|||
include_debug_in_info: Optional[bool] = None
|
||||
|
||||
|
||||
def _beijing_time_str() -> str:
|
||||
from datetime import datetime, timezone, timedelta
|
||||
|
||||
beijing_tz = timezone(timedelta(hours=8))
|
||||
return datetime.now(tz=beijing_tz).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
|
||||
@router.post("/logs/test-write")
|
||||
async def logs_test_write(
|
||||
x_admin_token: Optional[str] = Header(default=None, alias="X-Admin-Token"),
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
写入 3 条测试日志到 Redis(error/warning/info),用于验证“是否写入到同一台 Redis、同一组 key”。
|
||||
"""
|
||||
_require_admin(os.getenv("SYSTEM_CONTROL_TOKEN", "").strip(), x_admin_token)
|
||||
|
||||
client = _get_redis_client_for_logs()
|
||||
if client is None:
|
||||
raise HTTPException(status_code=503, detail="Redis 不可用,无法写入测试日志")
|
||||
|
||||
cfg = _read_logs_config(client)
|
||||
now_ms = int(__import__("time").time() * 1000)
|
||||
time_str = _beijing_time_str()
|
||||
|
||||
entries = {
|
||||
"error": {"level": "ERROR", "message": f"[TEST] backend 写入 error 测试日志 @ {time_str}"},
|
||||
"warning": {"level": "WARNING", "message": f"[TEST] backend 写入 warning 测试日志 @ {time_str}"},
|
||||
"info": {"level": "INFO", "message": f"[TEST] backend 写入 info 测试日志 @ {time_str}"},
|
||||
}
|
||||
|
||||
day = _beijing_yyyymmdd()
|
||||
stats_prefix = _logs_stats_prefix()
|
||||
|
||||
pipe = client.pipeline()
|
||||
for g in LOG_GROUPS:
|
||||
key = _logs_key_for_group(g)
|
||||
max_len = int(cfg["max_len"][g])
|
||||
entry = {
|
||||
"ts": now_ms,
|
||||
"time": time_str,
|
||||
"service": "backend",
|
||||
"level": entries[g]["level"],
|
||||
"logger": "api.routes.system",
|
||||
"message": entries[g]["message"],
|
||||
"hostname": os.getenv("HOSTNAME", ""),
|
||||
"signature": f"backend|{entries[g]['level']}|test|{entries[g]['message']}",
|
||||
"count": 1,
|
||||
}
|
||||
pipe.lpush(key, json.dumps(entry, ensure_ascii=False))
|
||||
if max_len > 0:
|
||||
pipe.ltrim(key, 0, max_len - 1)
|
||||
pipe.incr(f"{stats_prefix}:{day}:{g}", 1)
|
||||
pipe.expire(f"{stats_prefix}:{day}:{g}", 14 * 24 * 3600)
|
||||
|
||||
pipe.execute()
|
||||
|
||||
# 返回写入后的 LLEN,便于你确认
|
||||
pipe2 = client.pipeline()
|
||||
for g in LOG_GROUPS:
|
||||
pipe2.llen(_logs_key_for_group(g))
|
||||
llens = pipe2.execute()
|
||||
|
||||
return {
|
||||
"message": "ok",
|
||||
"keys": {g: _logs_key_for_group(g) for g in LOG_GROUPS},
|
||||
"llen": {g: int(llens[i] or 0) for i, g in enumerate(LOG_GROUPS)},
|
||||
"note": "如果你在前端仍看不到,说明前端请求的后端实例/Redis key/筛选条件不一致。",
|
||||
}
|
||||
|
||||
|
||||
def _get_redis_client_for_logs():
|
||||
"""
|
||||
获取 Redis 客户端(优先复用 config_manager 的连接;失败则自行创建)。
|
||||
|
|
|
|||
|
|
@ -113,6 +113,16 @@ export default function LogMonitor() {
|
|||
}
|
||||
}
|
||||
|
||||
const writeTest = async () => {
|
||||
setError('')
|
||||
try {
|
||||
await api.writeLogsTest()
|
||||
await load()
|
||||
} catch (e) {
|
||||
setError(e?.message || '写入测试日志失败')
|
||||
}
|
||||
}
|
||||
|
||||
const goFirstPage = () => setPageStart(0)
|
||||
const goNextPage = () => setPageStart(pageMeta?.next_start || 0)
|
||||
const goPrevPage = () => setPageStart(Math.max(0, pageStart - Number(limit || 200)))
|
||||
|
|
@ -130,6 +140,9 @@ export default function LogMonitor() {
|
|||
<button className="btn" onClick={load} disabled={loading}>
|
||||
刷新
|
||||
</button>
|
||||
<button className="btn" onClick={writeTest} disabled={loading}>
|
||||
写入测试日志
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -331,4 +331,16 @@ export const api = {
|
|||
}
|
||||
return response.json();
|
||||
},
|
||||
|
||||
writeLogsTest: async () => {
|
||||
const response = await fetch(buildUrl('/api/system/logs/test-write'), {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({ detail: '写入测试日志失败' }));
|
||||
throw new Error(error.detail || '写入测试日志失败');
|
||||
}
|
||||
return response.json();
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -65,10 +65,13 @@ logging.basicConfig(
|
|||
|
||||
# 追加:将 ERROR 日志写入 Redis(不影响现有文件/控制台日志)
|
||||
try:
|
||||
if __name__ == '__main__':
|
||||
from redis_log_handler import RedisErrorLogHandler, RedisLogConfig
|
||||
else:
|
||||
# 兼容两种启动方式:
|
||||
# - 直接运行:python trading_system/main.py
|
||||
# - 模块运行:python -m trading_system.main
|
||||
try:
|
||||
from .redis_log_handler import RedisErrorLogHandler, RedisLogConfig
|
||||
except Exception:
|
||||
from redis_log_handler import RedisErrorLogHandler, RedisLogConfig
|
||||
|
||||
redis_cfg = RedisLogConfig(
|
||||
redis_url=getattr(config, "REDIS_URL", "redis://localhost:6379"),
|
||||
|
|
@ -83,6 +86,22 @@ try:
|
|||
# 让 handler 自己按组筛选(error/warning/info),这里只需要放宽到 INFO
|
||||
redis_handler.setLevel(logging.INFO)
|
||||
logging.getLogger().addHandler(redis_handler)
|
||||
|
||||
# 诊断:启动时快速检测一次 Redis 可用性(失败不影响运行)
|
||||
try:
|
||||
client = redis_handler._get_redis() # noqa: SLF001(仅用于诊断)
|
||||
if client is None:
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.warning(
|
||||
f"⚠ Redis 日志写入未启用(无法连接或缺少依赖)。REDIS_URL={getattr(config, 'REDIS_URL', None)}"
|
||||
)
|
||||
else:
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.info(
|
||||
f"✓ Redis 日志写入已启用。REDIS_URL={getattr(config, 'REDIS_URL', None)}"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
# Redis handler 仅用于增强监控,失败不影响交易系统启动
|
||||
pass
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user