更新
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
"""Bound diagnostic payloads without changing live message processing."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
|
||||
TRUNCATION_MARKER = "\n...[日志内容过长,已截断]"
|
||||
_MESSAGE_FIELDS = (
|
||||
"type",
|
||||
"url",
|
||||
"uri",
|
||||
"text",
|
||||
"name",
|
||||
"width",
|
||||
"height",
|
||||
"duration",
|
||||
"sticker_id",
|
||||
"mime_type",
|
||||
)
|
||||
|
||||
|
||||
def _env_limit(name: str, default: int, minimum: int, maximum: int) -> int:
|
||||
try:
|
||||
value = int(os.getenv(name, str(default)) or default)
|
||||
except (TypeError, ValueError):
|
||||
value = default
|
||||
return max(minimum, min(maximum, value))
|
||||
|
||||
|
||||
def truncate_text(value: Any, limit: int) -> str:
|
||||
text = "" if value is None else str(value)
|
||||
if len(text) <= limit:
|
||||
return text
|
||||
keep = max(0, limit - len(TRUNCATION_MARKER))
|
||||
return text[:keep] + TRUNCATION_MARKER
|
||||
|
||||
|
||||
def bound_system_log_detail(value: Any) -> str:
|
||||
return truncate_text(
|
||||
value,
|
||||
_env_limit("KEFU_SYSTEM_LOG_MAX_CHARS", 4096, 512, 65536),
|
||||
)
|
||||
|
||||
|
||||
def _compact_media_message(value: Any) -> Any:
|
||||
if not isinstance(value, dict):
|
||||
return truncate_text(value, 2048)
|
||||
compact: dict[str, Any] = {}
|
||||
for key in _MESSAGE_FIELDS:
|
||||
if key not in value:
|
||||
continue
|
||||
item = value[key]
|
||||
compact[key] = truncate_text(item, 2048) if isinstance(item, str) else item
|
||||
compact["_log_truncated"] = True
|
||||
return compact
|
||||
|
||||
|
||||
def bound_message_log_content(value: Any) -> str:
|
||||
"""Keep a valid compact media JSON payload when a log entry is oversized."""
|
||||
limit = _env_limit("KEFU_MESSAGE_LOG_MAX_CHARS", 16384, 2048, 262144)
|
||||
text = "" if value is None else str(value)
|
||||
if len(text) <= limit:
|
||||
return text
|
||||
|
||||
try:
|
||||
parsed = json.loads(text)
|
||||
except (TypeError, ValueError, json.JSONDecodeError):
|
||||
return truncate_text(text, limit)
|
||||
|
||||
if isinstance(parsed, dict) and parsed.get("type"):
|
||||
compact = _compact_media_message(parsed)
|
||||
elif isinstance(parsed, dict) and isinstance(parsed.get("messages"), list):
|
||||
compact = {
|
||||
"messages": [
|
||||
_compact_media_message(item)
|
||||
for item in parsed["messages"][:20]
|
||||
],
|
||||
"_log_truncated": True,
|
||||
}
|
||||
else:
|
||||
return truncate_text(text, limit)
|
||||
|
||||
encoded = json.dumps(compact, ensure_ascii=False, separators=(",", ":"))
|
||||
return encoded if len(encoded) <= limit else truncate_text(encoded, limit)
|
||||
|
||||
|
||||
def bound_raw_message_log_content(value: Any) -> str:
|
||||
return truncate_text(
|
||||
value,
|
||||
_env_limit("KEFU_RAW_MESSAGE_LOG_MAX_CHARS", 32768, 4096, 262144),
|
||||
)
|
||||
|
||||
|
||||
def bound_error_log_content(value: Any) -> str:
|
||||
return truncate_text(
|
||||
value,
|
||||
_env_limit("KEFU_ERROR_LOG_MAX_CHARS", 4096, 512, 65536),
|
||||
)
|
||||
@@ -8,6 +8,7 @@ from typing import Optional
|
||||
|
||||
from models.database import AsyncSessionLocal
|
||||
from models.models import ReceivedMessageLog
|
||||
from utils.log_limits import bound_raw_message_log_content
|
||||
|
||||
logger = logging.getLogger("received_message_log")
|
||||
|
||||
@@ -23,8 +24,9 @@ async def record_received_message(
|
||||
message_type: Optional[int] = None,
|
||||
server_message_id: Optional[str] = None,
|
||||
) -> None:
|
||||
# 原样落库:不做 strip / parse / serialize,空字符串也记录
|
||||
store_content = raw_content if raw_content is not None else ""
|
||||
# The live message object remains untouched for matching/replying. Only
|
||||
# this diagnostic copy is bounded before persistence.
|
||||
store_content = bound_raw_message_log_content(raw_content)
|
||||
|
||||
async with AsyncSessionLocal() as db:
|
||||
try:
|
||||
|
||||
@@ -15,6 +15,8 @@ from collections import deque
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from .log_limits import bound_system_log_detail, truncate_text
|
||||
|
||||
logger = logging.getLogger("douyin_im.system")
|
||||
|
||||
_VALID_LEVELS = ("info", "success", "warning", "error")
|
||||
@@ -43,14 +45,16 @@ def record(
|
||||
"account_id": account_id,
|
||||
"level": level,
|
||||
"category": category,
|
||||
"event": str(event or ""),
|
||||
"detail": str(detail or ""),
|
||||
"event": truncate_text(event, 255),
|
||||
"detail": bound_system_log_detail(detail),
|
||||
"created_at": datetime.utcnow().isoformat(),
|
||||
}
|
||||
_buffer.appendleft(entry)
|
||||
_pending.append(entry)
|
||||
|
||||
msg = f"[{category}] {event}" + (f" | {detail}" if detail else "")
|
||||
msg = f"[{category}] {entry['event']}" + (
|
||||
f" | {entry['detail']}" if entry["detail"] else ""
|
||||
)
|
||||
if level == "error":
|
||||
logger.error(msg)
|
||||
elif level == "warning":
|
||||
|
||||
Reference in New Issue
Block a user