From 6a230cf6c8880b30d1c8cbc8e4e41b8a56761e0e Mon Sep 17 00:00:00 2001 From: Matthew Taylor Date: Thu, 30 Jul 2026 21:13:10 +0000 Subject: [PATCH] Fix isaaclab.* INFO logs silenced on kitless backends The isaaclab_info_stream log handler was installed only inside AppLauncher, which is skipped entirely for kitless backends (Newton, OvPhysX). As a result, logger.info() calls such as "Created new renderer for simulation" and "Using renderer" in RenderContext and Camera were silenced even though the handler was designed to surface them. Extract ensure_isaaclab_info_stream_handler() from AppLauncher into logging_utils so it can be shared. The kitless path in sim_launcher now calls it and raises the root logger to INFO (matching what AppLauncher already does for Kit-based backends), making isaaclab.* INFO records visible on the console for all backends. --- .../mataylor-fix-kitless-renderer-logging.rst | 10 ++++++ source/isaaclab/isaaclab/app/app_launcher.py | 22 ++++--------- source/isaaclab/isaaclab/app/logging_utils.py | 31 +++++++++++++++++++ source/isaaclab/isaaclab/app/sim_launcher.py | 14 +++++++-- 4 files changed, 59 insertions(+), 18 deletions(-) create mode 100644 source/isaaclab/changelog.d/mataylor-fix-kitless-renderer-logging.rst diff --git a/source/isaaclab/changelog.d/mataylor-fix-kitless-renderer-logging.rst b/source/isaaclab/changelog.d/mataylor-fix-kitless-renderer-logging.rst new file mode 100644 index 000000000000..d02d2a8848a5 --- /dev/null +++ b/source/isaaclab/changelog.d/mataylor-fix-kitless-renderer-logging.rst @@ -0,0 +1,10 @@ +Fixed +^^^^^ + +* Fixed ``logger.info`` calls in :class:`~isaaclab.renderers.RenderContext` and + :class:`~isaaclab.sensors.camera.Camera` (e.g. "Created new renderer for simulation" and + "Using renderer") being silenced on kitless backends (Newton, OvPhysX). + The ``isaaclab_info_stream`` log handler was only installed inside + :class:`~isaaclab.app.AppLauncher`, which is skipped for kitless runs. + :func:`~isaaclab.app.logging_utils.ensure_isaaclab_info_stream_handler` is now a + shared utility called by both the Kit and kitless launch paths. diff --git a/source/isaaclab/isaaclab/app/app_launcher.py b/source/isaaclab/isaaclab/app/app_launcher.py index fc178c44e12f..644da0dd3e2c 100644 --- a/source/isaaclab/isaaclab/app/app_launcher.py +++ b/source/isaaclab/isaaclab/app/app_launcher.py @@ -28,7 +28,11 @@ import isaacsim # noqa: F401 from isaacsim import SimulationApp -from isaaclab.app.logging_utils import apply_python_logging_level, resolve_python_logging_level +from isaaclab.app.logging_utils import ( + apply_python_logging_level, + ensure_isaaclab_info_stream_handler, + resolve_python_logging_level, +) from isaaclab.app.settings_manager import get_settings_manager, initialize_carb_settings from isaaclab.utils._device import set_cuda_device @@ -199,21 +203,7 @@ def _normalize_visualizer_intent(intent: Any) -> tuple[bool, bool]: @staticmethod def _ensure_isaaclab_info_stream_handler() -> None: """Add a stream handler for Isaac Lab INFO records hidden by Kit logging.""" - handler_name = "isaaclab_info_stream" - root_logger = logging.getLogger() - if any(getattr(handler, "name", None) == handler_name for handler in root_logger.handlers): - return - - class _IsaacLabInfoFilter(logging.Filter): - def filter(self, record: logging.LogRecord) -> bool: - return record.levelno == logging.INFO and record.name.startswith("isaaclab") - - handler = logging.StreamHandler(sys.stdout) - handler.name = handler_name - handler.setLevel(logging.INFO) - handler.addFilter(_IsaacLabInfoFilter()) - handler.setFormatter(logging.Formatter("[INFO]: %(message)s")) - root_logger.addHandler(handler) + ensure_isaaclab_info_stream_handler() def __init__(self, launcher_args: argparse.Namespace | dict | None = None, **kwargs): """Create a `SimulationApp`_ instance based on the input settings. diff --git a/source/isaaclab/isaaclab/app/logging_utils.py b/source/isaaclab/isaaclab/app/logging_utils.py index 6b087260cd05..39d483fd8830 100644 --- a/source/isaaclab/isaaclab/app/logging_utils.py +++ b/source/isaaclab/isaaclab/app/logging_utils.py @@ -58,3 +58,34 @@ def apply_python_logging_level(level: int) -> None: root_logger.setLevel(level) for handler in root_logger.handlers: handler.setLevel(level) + + +def ensure_isaaclab_info_stream_handler() -> None: + """Add a stream handler that surfaces Isaac Lab INFO records on stdout. + + Kit's Python log bridge suppresses INFO records on the console; this handler + compensates by routing ``isaaclab.*`` INFO records directly to stdout regardless + of what Kit does. The function is idempotent — calling it multiple times installs + the handler at most once. + + After calling this, callers that want ``isaaclab.*`` INFO visible at the default + WARNING log level must also raise the root logger to INFO:: + + ensure_isaaclab_info_stream_handler() + logging.getLogger().setLevel(logging.INFO) + """ + handler_name = "isaaclab_info_stream" + root_logger = logging.getLogger() + if any(getattr(h, "name", None) == handler_name for h in root_logger.handlers): + return + + class _IsaacLabInfoFilter(logging.Filter): + def filter(self, record: logging.LogRecord) -> bool: + return record.levelno == logging.INFO and record.name.startswith("isaaclab") + + handler = logging.StreamHandler(sys.stdout) + handler.name = handler_name + handler.setLevel(logging.INFO) + handler.addFilter(_IsaacLabInfoFilter()) + handler.setFormatter(logging.Formatter("[INFO]: %(message)s")) + root_logger.addHandler(handler) diff --git a/source/isaaclab/isaaclab/app/sim_launcher.py b/source/isaaclab/isaaclab/app/sim_launcher.py index ca7ec26e9aa3..5f1f9af77253 100644 --- a/source/isaaclab/isaaclab/app/sim_launcher.py +++ b/source/isaaclab/isaaclab/app/sim_launcher.py @@ -27,7 +27,11 @@ from isaaclab_physx.physics import PhysxCfg from isaaclab_physx.renderers import IsaacRtxRendererCfg -from isaaclab.app.logging_utils import apply_python_logging_level, resolve_python_logging_level +from isaaclab.app.logging_utils import ( + apply_python_logging_level, + ensure_isaaclab_info_stream_handler, + resolve_python_logging_level, +) from isaaclab.physics.physics_manager_cfg import PhysicsCfg from isaaclab.renderers.renderer_cfg import RendererCfg from isaaclab.sensors.camera.camera_cfg import CameraCfg @@ -439,7 +443,13 @@ def launch_simulation( # Kit-based backends apply the Python logging level inside AppLauncher; kitless backends # never construct it, so honor --verbose / --info here to keep behavior consistent. if not needs_kit: - apply_python_logging_level(resolve_python_logging_level(launcher_args)) + level = resolve_python_logging_level(launcher_args) + apply_python_logging_level(level) + ensure_isaaclab_info_stream_handler() + if level >= logging.WARNING: + # Root logger is at WARNING; raise it to INFO so that isaaclab.* INFO + # records can propagate to the scoped stream handler above. + logging.getLogger().setLevel(logging.INFO) if needs_kit and config_scan.has_kit_camera and launcher_args is not None: if not _get_arg(launcher_args, "enable_cameras", False):