ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
3

[ROS2] Best practice for catching all exceptions for logging

asked 2021-11-08 10:43:41 -0500

bvaningen gravatar image

updated 2021-11-10 04:20:51 -0500

I am creating a robot with ROS2 foxy that is in the field, and therefore not monitored all the time.
To help with debugging any eventual bugs/crashes that are unaccounted for, I was thinking of logging any tracebacks that occur. There are two approaches that I am considering:

Catch and except rclpy.spin

original post code

import rclpy
import traceback
from .example_node import NodeClass

def main(args=None):
    rclpy.init(args=args)

    node_class = NodeClass()

    try:
        rclpy.spin(node_class)
    except Exception:
        node_class.get_logger().error(traceback.format_exc())
    finally:
        # Destroy the node explicitly
        # (optional - otherwise it will be done automatically
        # when the garbage collector destroys the node object)
        node_class.destroy_node()
        rclpy.shutdown()

update post code after @Geoff comment

import rclpy
import traceback
from .example_node import NodeClass

def main(args=None):
    rclpy.init(args=args)
    traceback_logger = rclpy.logging.get_logger('node_class_traceback_logger')

    try:
        node_class = NodeClass()
        rclpy.spin(node_class)
    except Exception as exception:  
        traceback_logger.error(traceback.format_exc())
        raise exception

    finally:
        # Destroy the node explicitly
        # (optional - otherwise it will be done automatically
        # when the garbage collector destroys the node object)
        node_class.destroy_node()
        rclpy.shutdown()

Create a custom logger in launch file

from the example launch found here, a custom event handler can be created. This could potentially catch tracebacks.

# Setup a custom event handler for all stdout/stderr from processes.
# Later, this will be a configurable, but always present, extension to the LaunchService.
def on_output(event: launch.Event) -> None:
    for line in event.text.decode().splitlines():
        print('[{}] {}'.format(
            cast(launch.events.process.ProcessIO, event).process_name, line))

ld.add_action(launch.actions.RegisterEventHandler(launch.event_handlers.OnProcessIO(
    # this is the action     ^              and this, the event handler ^
    on_stdout=on_output,
    on_stderr=on_output,
)))

Which of these would be considered better practice? Or is there a better approach builtin ROS2?

Thanks in advance!

edit retag flag offensive close merge delete

2 Answers

Sort by ยป oldest newest most voted
0

answered 2022-01-27 08:56:03 -0500

bvaningen gravatar image

After some time I think that my current approach is best, which I will explain here for anyone who else may need it!

on the node side, the code is as followed:

import rclpy
import traceback
from .example_node import NodeClass
from rclpy.node import Node

def main(args=None):

    rclpy.init(args=args)

    try:
        node_class = NodeClass()
        rclpy.spin(node_class)

    except Exception as exception:  
        traceback_logger_node = Node('node_class_traceback_logger')
        traceback_logger_node.get_logger().error(traceback.format_exc())  
        raise exception

    else:
        # Destroy the node explicitly
        # (optional - otherwise it will be done automatically
        # when the garbage collector destroys the node object)
        node_class.destroy_node()
        rclpy.shutdown()

An import thing to note with this approach is that if you are using a launch file that you need to omit the name field, as shown below. Otherwise both nodes will be given the same name!

from launch_ros.actions import Node

from launch import LaunchDescription


def generate_launch_description():
    return LaunchDescription(
        [
            Node(
                package="system_monitor",
                executable="system_monitor_node",
                output={'both': {'screen', 'log', 'own_log'}},
            ),
        ]
    )
edit flag offensive delete link more
2

answered 2021-11-08 17:19:32 -0500

Geoff gravatar image

I don't think there is a "best practice" for what you're trying to do, but I do have a comment on your first option. Catching _any_ exception that comes out of rclpy.spin(node_class) followed by using node_class's logger is potentially unsafe. It's possible (although fairly unlikely) that node_class or its logger is in an unknown state due to an error when you try to log the traceback.

edit flag offensive delete link more

Comments

good point! I have updated the original post with an updated version of the code that uses a separate logger.

In a more abstract sense, do you think that the idea of catching all exceptions in such a manner is advisable, or is it symptomatic of bad code?

bvaningen gravatar image bvaningen  ( 2021-11-10 04:33:10 -0500 )edit

One drawback of the first method, is also that you can't log the exceptions if they occur in __init__, because the node's logger would not have been created

tnajjar gravatar image tnajjar  ( 2023-02-14 07:34:29 -0500 )edit

Question Tools

3 followers

Stats

Asked: 2021-11-08 10:43:41 -0500

Seen: 1,884 times

Last updated: Jan 27 '22