Log from your code ================== The stack logs through ``core_logger``, not through ``RCLCPP_*``, ``printf`` or ``std::cout``. One logger means one place to set levels, one format, and output that reaches both the console and ``rosout``. .. note:: ``core_utils`` is the deliberate exception — it sits below the logger and cannot depend on it. Basic use --------- .. code-block:: cpp #include LOG_INFO("arm enabled"); LOG_INFO_STREAM("moving to destination " << destination); LOG_WARN_STREAM("retrying after " << delay_s << " s"); LOG_ERROR_STREAM("plan failed: " << ErrorCodes::ToString(code)); Levels, quietest to loudest: ``FATAL``, ``ERROR``, ``WARN``, ``INFO``, ``DEBUG``, ``VERBOSE``. Each has a plain and a ``_STREAM`` form, and a ``_NAMED`` / ``_STREAM_NAMED`` variant that takes an explicit logger name. Set the level at runtime: .. code-block:: bash CORE_LOG_LEVEL=VERBOSE ros2 launch _bringup .launch.py Choosing a level ---------------- The test is who reads the line and how often it fires. .. list-table:: :header-rows: 1 :widths: 18 82 * - Level - Use for * - ``ERROR`` - The operation failed and the caller must handle it. * - ``WARN`` - Something unexpected that the system recovered from. **Not** for outcomes that are routine — a planner returning "start and goal too close" when the arm is already at the goal is expected, and belongs at ``INFO``. * - ``INFO`` - Milestones an operator would want in a normal run. One line per phase, not per trajectory point. * - ``DEBUG`` - Detail for diagnosing a specific subsystem. * - ``VERBOSE`` - Per-iteration and per-message detail, including protocol traffic. .. warning:: Anything that fires per trajectory point or per network message belongs at ``VERBOSE``. A per-point ``INFO`` line makes the console unusable and hides the lines that matter. In headers ---------- The bare ``LOG_*`` macros do not work in a header that is included from another package. They resolve ``::logger``, a non-dependent name that may not exist at the include site. Use the ``_NAMED`` form and pass the logger explicitly: .. code-block:: cpp // in a header LOG_INFO_STREAM_NAMED("my_component", "value: " << value); Inside ROS nodes ---------------- ``core_logger``'s ``RclutilsAppender`` bridges log output onto ``rosout``, so ``ros2 topic echo /rosout`` and Foxglove see the same lines as the console. .. warning:: Detach the appender before the node shuts down or the plugin unloads. ``dlclose`` on a plugin and ``rclcpp`` shutdown both segfault the host process if an attached appender outlives the context it writes to. ``ScopedRootLogger`` handles this — it attaches on construction and detaches on destruction. Prefer it to attaching by hand. Checking your work ------------------ Before opening a PR, confirm nothing slipped back in: .. code-block:: bash grep -rnE 'RCLCPP_(INFO|WARN|ERROR|DEBUG|FATAL)|printf\(|std::cout' \ --include=*.cpp --include=*.hpp src/ | grep -v core_utils