fix: delete agent 500 error + dynamic personality + deployment guide
- Fix delete agent 500: clean up FK records (agent_llm_logs, permissions, schedules, executions, team_members) and unbind goals/tasks before delete - Remove hardcoded personality templates in Android, replace with dynamic system prompt generation from name + description - Set promptSectionsEnabled=false to bypass PromptComposer for personality - Add Tencent Cloud Linux deployment guide (Docker Compose) - Accumulated backend service updates, frontend UI fixes, Android app changes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
76
backend/app/core/logging_config.py
Normal file
76
backend/app/core/logging_config.py
Normal file
@@ -0,0 +1,76 @@
|
||||
"""
|
||||
JSON 结构化日志配置 — 用于 ELK 日志聚合。
|
||||
|
||||
用法: 在 main.py 启动时调用 setup_json_logging() 即可。
|
||||
会在 logs/ 目录下并行输出 app.json.log(JSON 格式)。
|
||||
现有文本格式日志不受影响。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from datetime import datetime, timezone
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from app.core.config import settings
|
||||
|
||||
|
||||
class JsonFormatter(logging.Formatter):
|
||||
"""将日志记录格式化为单行 JSON,便于 Filebeat → Elasticsearch 采集。"""
|
||||
|
||||
def format(self, record: logging.LogRecord) -> str:
|
||||
log_entry: dict[str, Any] = {
|
||||
"timestamp": datetime.fromtimestamp(
|
||||
record.created, tz=timezone.utc
|
||||
).isoformat(),
|
||||
"level": record.levelname,
|
||||
"logger": record.name,
|
||||
"message": record.getMessage(),
|
||||
"module": record.module,
|
||||
"function": record.funcName,
|
||||
"line": record.lineno,
|
||||
}
|
||||
|
||||
# 异常信息
|
||||
if record.exc_info and record.exc_info[1]:
|
||||
log_entry["exception"] = self.formatException(record.exc_info)
|
||||
|
||||
# 上下文字段(如 request_id / user_id)
|
||||
for key in ("request_id", "user_id", "workspace_id", "client_ip", "method", "path", "status_code", "duration_ms"):
|
||||
val = getattr(record, key, None)
|
||||
if val is not None:
|
||||
log_entry[key] = val
|
||||
|
||||
return json.dumps(log_entry, ensure_ascii=False, default=str)
|
||||
|
||||
|
||||
def setup_json_logging() -> None:
|
||||
"""为 root logger 添加 JSON 格式的 RotatingFileHandler。
|
||||
|
||||
日志写入 LOG_DIR/app.json.log,大小达到 LOG_MAX_BYTES 时轮转。
|
||||
"""
|
||||
log_dir = Path(settings.LOG_DIR)
|
||||
log_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
json_log_path = log_dir / "app.json.log"
|
||||
|
||||
# 避免重复添加(uvicorn reload 时会重新执行 startup)
|
||||
root = logging.getLogger()
|
||||
for h in root.handlers:
|
||||
if isinstance(h, RotatingFileHandler) and str(json_log_path) in getattr(h, 'baseFilename', ''):
|
||||
return
|
||||
|
||||
handler = RotatingFileHandler(
|
||||
json_log_path,
|
||||
maxBytes=settings.LOG_MAX_BYTES,
|
||||
backupCount=settings.LOG_BACKUP_COUNT,
|
||||
encoding="utf-8",
|
||||
)
|
||||
handler.setFormatter(JsonFormatter())
|
||||
handler.setLevel(getattr(logging, settings.LOG_LEVEL.upper(), logging.INFO))
|
||||
|
||||
root.addHandler(handler)
|
||||
logging.getLogger(__name__).info("JSON 日志已启用 → %s", json_log_path)
|
||||
Reference in New Issue
Block a user