from __future__ import annotations from flask import has_request_context, request, current_app from typing import Optional import json from flask_jwt_extended import current_user from iti_system.models import SysLog from iti.applications.extensions.db import db from iti.applications.common.enums import LogType from iti.applications.common.utils.sys_log_helper import ( summarize_response_json, safe_json_dumps, truncate_text, ) def add_sys_log( name: str, *, type: "LogType | str" = LogType.OPERATION, desc: Optional[str] = None, success: Optional[bool] = None, response: Optional[dict | str] = None, exception: Optional[Exception | str] = None, extra: Optional[dict] = None, ) -> None: """ 手动新增系统日志(请求内/外均可调用)。 - 共用 after_request 的摘要策略与配置项 - 参数尽量少,自动采集请求上下文信息(如有) """ max_body_chars = ( int(current_app.config.get("SYSLOG_MAX_BODY_CHARS", 2048)) if current_app else 2048 ) items_sample_size = ( int(current_app.config.get("SYSLOG_ITEMS_SAMPLE", 5)) if current_app else 5 ) method = path = user_agent = ip = None headers = query = body = None user_id = None if has_request_context(): method = request.method path = request.path headers = {k: request.headers.getlist(k) for k in request.headers.keys()} query = request.args.to_dict() body = request.get_json(silent=True) or {} user_id = current_user.id if current_user else None user_agent = request.headers.get("User-Agent", "None") ip = request.remote_addr or request.headers.get("X-Forwarded-For") resp_text: Optional[str] = None if isinstance(response, dict): summarized = summarize_response_json(response, items_sample_size) resp_text = safe_json_dumps( summarized, ensure_ascii=False, max_chars=max_body_chars ) elif isinstance(response, str): resp_text = truncate_text(response, max_body_chars) if success is None: success = False if exception else None log = SysLog( name=name, desc=desc or "", type=( type.value if isinstance(type, LogType) else (type or LogType.OPERATION.value) ), method=method, user_id=user_id, path=path, ip=ip, user_agent=user_agent, headers=json.dumps(headers, ensure_ascii=False) if headers is not None else None, query_params=json.dumps(query, ensure_ascii=False) if query is not None else None, body_params=json.dumps(body, ensure_ascii=False) if body is not None else None, execution_time=None, response=resp_text, success=success, exception=str(exception) if exception else None, ) db.session.add(log) db.session.commit()