-
Notifications
You must be signed in to change notification settings - Fork 330
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace print() in python demos with logger calls #190
Changes from all commits
d74e6f9
b691f0e
e0e2b13
869a02d
9b4cec2
83274c4
bbdcaa3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,9 +39,11 @@ def change_state(lifecycle_node, change_state_args=''): | |
cli.call(req) | ||
cli.wait_for_future() | ||
if cli.response.success: | ||
print('%s successfully triggered transition %s' % (lifecycle_node, change_state_args)) | ||
node.get_logger().info( | ||
'%s successfully triggered transition %s' % (lifecycle_node, change_state_args)) | ||
else: | ||
print('%s failed to triggered transition %s' % (lifecycle_node, change_state_args)) | ||
node.get_logger().info( | ||
'%s failed to triggered transition %s' % (lifecycle_node, change_state_args)) | ||
|
||
|
||
def get_state(lifecycle_node): | ||
|
@@ -51,8 +53,9 @@ def get_state(lifecycle_node): | |
req = GetState.Request() | ||
cli.call(req) | ||
cli.wait_for_future() | ||
print('%s is in state %s(%u)' | ||
% (lifecycle_node, cli.response.current_state.label, cli.response.current_state.id)) | ||
node.get_logger().info( | ||
'%s is in state %s(%u)' | ||
% (lifecycle_node, cli.response.current_state.label, cli.response.current_state.id)) | ||
|
||
|
||
def get_available_states(lifecycle_node): | ||
|
@@ -62,9 +65,10 @@ def get_available_states(lifecycle_node): | |
req = GetAvailableStates.Request() | ||
cli.call(req) | ||
cli.wait_for_future() | ||
print('%s has %u available states' % (lifecycle_node, len(cli.response.available_states))) | ||
node.get_logger().info( | ||
'%s has %u available states' % (lifecycle_node, len(cli.response.available_states))) | ||
for state in cli.response.available_states: | ||
print('id: %u\tlabel: %s' % (state.id, state.label)) | ||
node.get_logger().info('id: %u\tlabel: %s' % (state.id, state.label)) | ||
|
||
|
||
def get_available_transitions(lifecycle_node): | ||
|
@@ -75,24 +79,24 @@ def get_available_transitions(lifecycle_node): | |
req = GetAvailableTransitions.Request() | ||
cli.call(req) | ||
cli.wait_for_future() | ||
print('%s has %u available transitions' | ||
% (lifecycle_node, len(cli.response.available_transitions))) | ||
node.get_logger().info( | ||
'%s has %u available transitions' | ||
% (lifecycle_node, len(cli.response.available_transitions))) | ||
for transition in cli.response.available_transitions: | ||
print('Transition id: %u\tlabel: %s' | ||
% (transition.transition.id, transition.transition.label)) | ||
print('\tStart id: %u\tlabel: %s' | ||
% (transition.start_state.id, transition.start_state.label)) | ||
print('\tGoal id: %u\tlabel: %s' | ||
% (transition.goal_state.id, transition.goal_state.label)) | ||
node.get_logger().info( | ||
'Transition id: %u\tlabel: %s' | ||
% (transition.transition.id, transition.transition.label)) | ||
node.get_logger().info( | ||
'\tStart id: %u\tlabel: %s' | ||
% (transition.start_state.id, transition.start_state.label)) | ||
node.get_logger().info( | ||
'\tGoal id: %u\tlabel: %s' | ||
% (transition.goal_state.id, transition.goal_state.label)) | ||
|
||
|
||
def main(service_type, lifecycle_node, change_state_args='', args=None): | ||
rclpy.init(args=args) | ||
|
||
if not rclpy.ok(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not necessary anymore because init would raise on failure ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yep, thanks for making it explicit 😄 |
||
print('Something is wrong with rclpy init') | ||
return | ||
|
||
if service_type == 'change_state': | ||
change_state(lifecycle_node, change_state_args) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ | |
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import os | ||
import sys | ||
|
||
from launch import LaunchDescriptor | ||
|
@@ -25,6 +26,10 @@ def main(): | |
package = 'topic_monitor' | ||
executable = get_executable_path(package_name=package, executable_name='data_publisher') | ||
|
||
# Strip the logger name from the message format in favor of the shorter executable name | ||
os.environ['RCUTILS_CONSOLE_OUTPUT_FORMAT'] = '[{severity}] {message}' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this modified environment need to be passed to the launched processes ? (maybe it's not necessary as the same environment is used for the launchfile and each launched process). Otherwise there's an example of custom environment passed to launched processes here: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah any children processes will inherit this environment automatically There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
os.environ['PYTHONUNBUFFERED'] = '1' # force unbuffered output to get prints to sync correctly | ||
|
||
launch_descriptor.add_process( | ||
cmd=[executable, 'sensor', '--best-effort'], | ||
name='sensor', | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not obvious to me that this is actually performing a logging action compared to the API suggested originally in ros2/rclpy#103, maybe just calling the function
loginfo
or alog
function taking a severity keyword would be clearer.I know the recent discussion was focused on the c++ API but how do we expect the API to look like for the users in python?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the
.log(,INFO)
option is also available on this logger, following ros2/rclpy#103 (comment) (which came from a team discussion),.info()
is offered for convenience. Both options mirror what is offered by python logging, we seem to prefer the.info()
: https://github.com/ros2/ros2cli/blob/9fce6a08381bb67cf17e7160b0b2c6556ffec1e6/ros2cli/ros2cli/entry_points.py#L87There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok so the goal is to use an API as close as python native logging as possible rather than something closer to ROS 1. We do provide both but will use this one in our examples. Fair enough.
With ros2/rclpy#148 (comment) it now make sense why we don't something like
self.mylogger.loginfo()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Following up on this, if I understand correctly your initial concern was referring to how we would use the generic logging functions (e.g.
rclpy.logging.loginfo()
) as opposed to log calls being made on specific logger objects (which weren't around in ROS 1).On the use of
mylogger.info()
as opposed tomylogger.log(, severity=INFO)
, as you noted I've chosen to mirror the typical usage of native python logging. https://docs.python.org/3/howto/logging-cookbook.html, for example, usesmylogger.info()
in all cases except where the severity is chosen programatically, for which case themylogger.log(, severity=INFO)
form is available