launch icon indicating copy to clipboard operation
launch copied to clipboard

[launch] not wiring python prints to stdout in realtime

Open stonier opened this issue 6 years ago • 14 comments

Bug report

Required Info:

  • Operating System:
    • Ubuntu 18.04
  • Installation type:
    • binaries
  • Version or commit hash:
    • Crystal
  • DDS implementation:
    • Default
  • Client library (if applicable):
    • rclpy

Steps to reproduce issue

Add them both to a launcher:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import launch
import launch_ros.actions

def generate_launch_description():
    launch_description = launch.LaunchDescription()
    launch_description.add_action(
        launch_ros.actions.Node(
            package='demo_nodes_cpp', node_executable='talker', output='screen'),
    )
    launch_description.add_action(
        launch_ros.actions.Node(
            package='demo_nodes_py', node_executable='talker', output='screen'),
    )
    return launch_description

and call ros2 launch ... on it.

Expected behavior

Both logged messages and cout/print messages make it in realtime to stdout.

Actual behavior

All make it there except the python print statements. They only show on stdout after interrupting the launch with CTRL-C.

stonier avatar Feb 18 '19 05:02 stonier

I think the difference is that we intentionally flush the out when logging:

https://github.com/ros2/rcutils/blob/a7581a333444c2804f2b7c27dd0da1b2946bebff/src/logging.c#L702

There is inherent buffing of stdout when a process is not connected to a tty.

If you changed this:

import launch
import launch_ros.actions

def generate_launch_description():
    launch_description = launch.LaunchDescription()
    launch_description.add_action(
        launch_ros.actions.Node(
            package='demo_nodes_cpp', node_executable='talker', output='screen'),
    )
    launch_description.add_action(
        launch_ros.actions.Node(
            package='demo_nodes_py', node_executable='talker', output='screen',
            env={PYTHONUNBUFFERED : "1"}),
    )
    return launch_description

I expect that you'll see prompt printing.


It's possible that the emulate_tty option in osrf_pycommon's API could address this, but I didn't enable it by default. You could do it here:

https://github.com/ros2/launch/blob/e8704247708eb017c388aaf8606e9dbb6971239b/launch/launch/actions/execute_process.py#L439

wjwwood avatar Feb 19 '19 09:02 wjwwood

emulate_tty=True works if a bugfix goes into this code:

osrf_pycommon/process_utils/async_execute_process_asyncio/impl.py#L67-L68

so that keyword arguments are utilised (otherwise you'll get positional argument errors):

    return protocol_class(stdin=None, stdout=stdout_master, stderr=stderr_master)

Are there consequences of emulate_tty you are wary of or would you like me to send a couple of PR's your way for this?

stonier avatar Feb 22 '19 03:02 stonier

emulate_tty does cause extra overhead, in that it needs to create a pty pair for stdout and stderr of each subprocess. So in situations like with catkin_tools you could run out of pty's on your system. This is usually configurable in kernel settings or similar, and I believe osrf_pycommon will gracefully degrade to not using emulate_tty when no more pty are available. There's also the possibility of pty's getting left over, if launch or Python were to segfault for instance. This can be annoying to notice and fix (usually just restart), but unless you're developing launch and causing new bugs, ideally that wouldn't be an issue.

That all being said, I think it's worth turning on by default and exposing a emulate_tty option in the ExecuteProcess action so people can turn it off if they want. It would also be good to have ExecuteProcess read a launch configuration, e.g. like emulate_tty_by_default, which if you set to false would globally avoid emulate_tty unless explicitly enabled. You can see an example of this here:

https://github.com/ros2/launch/blob/e8704247708eb017c388aaf8606e9dbb6971239b/launch/launch/actions/execute_process.py#L162-L164

In that case you can either specify a prefix manually for a single invocation of ExecuteProcess or set the prefix for an entire launch description (or part of one) using the launch-prefix Launch Configuration.

wjwwood avatar Feb 22 '19 04:02 wjwwood

@stonier What's the status of this? Did #52 provide any steps towards resolving this?

mjcarroll avatar Mar 14 '19 14:03 mjcarroll

@mjcarroll you're pointing to a very old PR (2017). Was that intended?

I've got the bugfix PR in that enables the solution proposed by @wjwwood to be attempted but haven't had the cycles to contribute that PR yet.

stonier avatar Mar 15 '19 11:03 stonier

I'm not totally sure but is this the same issue that's causing us to not see any stdout output at all while the node is running? (https://answers.ros.org/question/332829/no-stdout-logging-output-in-ros2-using-launch/) If so this seems like a serious problem that needs to be escalated.

jdlangs avatar Sep 13 '19 22:09 jdlangs

Unfortunately, this setting is not on by default yet, as it causes all kinds of other issues, but you can enable it yourself and probably fix the problem. If using emulate_tty=True in the Node/ExecuteProcess actions of your Python launch does not fix the problem, then it's probably something else.

wjwwood avatar Sep 13 '19 22:09 wjwwood

This is the pr which is stalled at the moment because I don't have time to pursue it which enables emulate_tty by default, which would "convince" subprocesses that they're attached to a terminal and do line buffering without any configuration:

https://github.com/ros2/launch/pull/286

We should address all the issues that cause this, but honestly even if we could enable it by default, I'm worried about the performance in a large system. As was the conclusion in other threads, e.g. https://github.com/ros2/rcutils/pull/169#issuecomment-513410813, we may want to leave this off by default or use some heuristic like: only turning it on if output=screen, or just for stderr, or maybe only if there are a small number of subprocesses or something. And then provide a convenient way to enable/disable it globally or selectively.

wjwwood avatar Sep 13 '19 23:09 wjwwood

@wjwwood, that's the wrong fix. User-readable debug output should go to stderr. If you're worried about performance, that's what log_level is for

rotu avatar Sep 13 '19 23:09 rotu

Which fix? I don't think I said debug output shouldn't go to stderr. Of course you can do things to fix performance on the application side (print less or adjust the log level), but there's no reason for launch to log stdout/stderr to file with line buffering instead of full buffering...

wjwwood avatar Sep 13 '19 23:09 wjwwood

Sorry, nevermind. I thought you were aiming to fix the similar issue https://github.com/ros2/rcutils/issues/168.

rotu avatar Sep 13 '19 23:09 rotu

No, that probably needs to change as you guys have advocated. Just don't have time to push on it atm.

wjwwood avatar Sep 13 '19 23:09 wjwwood

Confirmed that enabling emulate_tty myself fixed my issue. I would support changing the default to prefer usability over performance, especially now that it's configurable on the user's end.

jdlangs avatar Sep 16 '19 16:09 jdlangs

Solution given the above discussion:

launch_ros.actions.Node(
            package='demo_nodes_py', 
            node_executable='talker', 
            output='screen',
            emulate_tty=True
    )

lorepieri8 avatar Mar 17 '23 14:03 lorepieri8