The ``session_logging.py`` module ================================= .. py:module:: ansys.cfx.mcp.common.session_logging Summary ------- .. py:currentmodule:: session_logging .. tab-set:: .. tab-item:: Functions .. list-table:: :header-rows: 0 :widths: auto * - :py:obj:`~register_log_root` - Register an extra logger root for the session file handler. * - :py:obj:`~init_session_logging` - Set up a session log folder and attach a file handler. * - :py:obj:`~get_session_log_dir` - Return the active session log directory, if any. * - :py:obj:`~get_latest_log_path` - Return the path to ``/latest.log`` if it exists. * - :py:obj:`~get_latest_session_dir` - Return the path from ``/latest_session.txt``, if present. * - :py:obj:`~shutdown_session_logging` - Detach and close the session file handler. .. tab-item:: Constants .. list-table:: :header-rows: 0 :widths: auto * - :py:obj:`~ENV_ENABLE` - * - :py:obj:`~ENV_DISABLE` - * - :py:obj:`~ENV_BASE_DIR` - * - :py:obj:`~ENV_LEVEL` - Description ----------- Per-session debug logging for CFX-MCP. When the gateway (or any long-running entry point) starts, this module creates a per-session folder and attaches a DEBUG-level file handler to this package's root logger (``ansys.cfx.mcp``). An optional higher-level layer that shares the process can add its own root via :func:`register_log_root` so a single ``session.log`` captures the whole stack — this package never names or imports that layer. All child loggers propagate up to the handler, so tool calls, plan executions, run_code snippets, LLM round-trips, and validator decisions are all captured in one place without touching individual call sites. Goals ----- * **Self-service diagnostics** — when a user reports a problem we can ask them to attach the session folder and have the full picture (logs, recorded tool args, last `run_code` snippets via the existing RunLogger, environment snapshot). * **Off by default for embedded use** — the library import path stays silent. The handler is only attached when an entry point explicitly calls :func:`init_session_logging` (the gateway and the CLI both do). * **Easy to disable** — setting ``FLUIDS_MCP_DISABLE_SESSION_LOGS=1`` short-circuits the call. Safe in regulated environments where local log files must not be created. Layout ------ Default base directory: * Windows: ``%APPDATA%\\Ansys\\v271\\cfx\\aisol`` * macOS / Linux: ``~/.ansys/v271/cfx/aisol`` Override with ``FLUIDS_MCP_SESSION_LOG_DIR=`` (the path is used verbatim — sessions still get their own subfolders inside it). Each session creates ``//`` containing: * ``session.log`` — DEBUG-level text log of every ``ansys.cfx.mcp.*`` module (plus any extra roots registered via :func:`register_log_root`) through Python's logging. * ``env.txt`` — snapshot of process env vars relevant to the server (``FLUIDS_*``, ``ANSYS_*``, ``AWP_ROOT*``, ``AALI_*``, ``LLM_*``) plus Python/OS versions. * ``meta.json`` — small JSON file with session id, start time, log base, gateway host/port (if known). The :class:`agent.loop.run_log.RunLogger` continues to write per-conversation JSONL files under ``/runs/`` exactly as before — the new session folder is for *cross-cutting* diagnostics, not per-conversation event streams. .. !! processed by numpydoc !! Module detail ------------- .. py:function:: register_log_root(name: str) -> None Register an extra logger root for the session file handler. Inversion-of-control hook: a higher-level layer running in the same process (e.g. an agent product with its own top-level package logger) calls this at import time to have its records captured in the same ``session.log`` — without this package ever importing or naming that layer. Idempotent. If a session handler is already attached, the new root is wired up immediately so registration order does not matter. :Parameters: **name** : :class:`python:str` Name of the object, module, or setting being processed. :Returns: :data:`python:None` The function completes through its side effects. .. !! processed by numpydoc !! .. py:function:: init_session_logging(*, session_id: Optional[str] = None, extra_meta: Optional[dict[str, object]] = None) -> Optional[pathlib.Path] Set up a session log folder and attach a file handler. Returns the resolved session directory, or ``None`` when disabled via :data:`ENV_DISABLE`. Safe to call multiple times — subsequent calls return the existing directory without re-attaching handlers. :Parameters: **session_id** : :obj:`Optional`\[:class:`python:str`] Session id supplied to the function. **extra_meta** : :obj:`Optional`\[:class:`python:dict`\[:class:`python:str`, :obj:`object`]] Extra meta supplied to the function. :Returns: :obj:`Optional`\[:obj:`Path`] Result produced by the function. .. !! processed by numpydoc !! .. py:function:: get_session_log_dir() -> Optional[pathlib.Path] Return the active session log directory, if any. :Returns: :obj:`Optional`\[:obj:`Path`] Result produced by the function. .. !! processed by numpydoc !! .. py:function:: get_latest_log_path() -> Optional[pathlib.Path] Return the path to ``/latest.log`` if it exists. This is the stable pointer the user can ``Get-Content`` / ``tail`` without needing to know the session id. Returns ``None`` if session logging has never run on this machine. :Returns: :obj:`Optional`\[:obj:`Path`] Result produced by the function. .. !! processed by numpydoc !! .. py:function:: get_latest_session_dir() -> Optional[pathlib.Path] Return the path from ``/latest_session.txt``, if present. Use this from a separate process (e.g. a CLI command) to find the most recent ``serve`` session directory without scanning timestamps. :Returns: :obj:`Optional`\[:obj:`Path`] Result produced by the function. .. !! processed by numpydoc !! .. py:function:: shutdown_session_logging() -> None Detach and close the session file handler. Used by tests; in production the handler outlives the process by design. :Returns: :data:`python:None` The function completes through its side effects. .. !! processed by numpydoc !! .. py:data:: ENV_ENABLE :value: 'FLUIDS_MCP_SESSION_LOGS' .. py:data:: ENV_DISABLE :value: 'FLUIDS_MCP_DISABLE_SESSION_LOGS' .. py:data:: ENV_BASE_DIR :value: 'FLUIDS_MCP_SESSION_LOG_DIR' .. py:data:: ENV_LEVEL :value: 'FLUIDS_MCP_SESSION_LOG_LEVEL'