The ``session_logging.py`` module ================================= .. py:module:: ansys.fluent.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 PyFluent-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.fluent.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, client turns, and validator decisions are all captured in one place without touching individual call sites. Goals ----- * **Self-service diagnostics**: When reporting a problem, a user is asked to attach the session folder and have the full picture (logs, recorded tool arguments, and 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\\fluent\\aisol`` * macOS / Linux: ``~/.ansys/v271/fluent/aisol`` Override with ``FLUIDS_MCP_SESSION_LOG_DIR=``. (The path is used verbatim. The sessions still get their own subfolders inside it). Each session creates ``//`` containing: * ``session.log``: DEBUG-level text log of every ``ansys.fluent.mcp.*`` module (plus any extra roots registered via :func:`register_log_root`) through Python's logging. * ``env.txt``: Snapshot of process environment variables relevant to the server (``FLUIDS_*``, ``ANSYS_*``, ``AWP_ROOT*``, ``PYFLUENT_*``, ``FLUENT_*``) plus Python/OS/PyFluent versions. * ``meta.json``: Small JSON file with session identifier, start time, log base, and 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 (such as 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 identifier to supply to the function. **extra_meta** : :obj:`Optional`\[:class:`python:dict`\[:class:`python:str`, :obj:`object`]] Extra metadata to supply 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 identifier. 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 (such as 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'