I have used ROS (1+2) for many years and also teached courses with it in university. Most students or people who used ROS for the first time have difficulties to find out what the problem is if their robot is not doing what they expected. When I was asked for help, I just scrolled through the terminal where they have executed their launch and in the vast majority of cases found the problem in a few seconds, because it was something simple like a node crashing on startup. Of course, there was an error message saying what the problem is, but somehow people new to ROS (even those with a lot of coding experience) had trouble seeing it. I also remember many times where it took me a lot of time to actually find a relevant output.
So what is the problem? I think there is just too much spam in the terminal output which makes it really hard to get an overview. In this post I would like to discuss different ways on how to improve this.
I know, that it is possible to use tools like rqt_console to better structure the logging output. But I think there are some good reasons why people sometimes rely on the terminal output:
- See general error messages that are not ROS logging (e.g. exceptions from Python)
- Get a quick feedback after “ros2 run” or “ros2 launch” without switching windows
- Working without GUI, e.g. via SSH
To better visualize the problem look at this screenshot.
The first is the output of starting the logging_demo which just starts a single node that sends some logging messages. Half of the output does not provide information:
- The log level is already reflected in the color, writing [Info] is redundant.
- I have never used the timestamp in the terminal log for anything but it takes away a lot of space. I is also very hard to read as a human.
- The node name can be important in some cases, to know where the message is coming from. However, in my experience, most of the time I already know based on the error message which node it is. Even if I am interested in it, I would like to read the messages first to see which are interesting.
It gets worse when we use launch, as we can see in the next screenshot.
Now launch adds another tag. This one is basically the same as the name of the node and does not provide any information. There are some cases where it does, but again in the majority of cases it is just spam. Another problem, in my oppinion, is that due to the different length of the node names, the start of the actual message on the horizontal axis does not align. This makes it already harder to read. In this example the length difference is not so large, it can be much worse.
So how can we improve this?
You can influence the behavior of the logging using the environment variable RCUTILS_CONSOLE_OUTPUT_FORMAT (see Logging and logger configuration — ROS 2 Documentation: Rolling documentation).
The default is “[{severity}] [{time}] [{name}]: {message}” and leads to outputs like above. We can easily change this to tackle the problems mentioned above. The output is much more readable in my opinion.
If we really need additional information, we can also put it after our message and the node name.
However, if we start something with launch, we will still end up with the extra tag that is introduced by launch.
Now we will have to get a bit more creative and use ANSI control sequences. (I also proposed to have an environment variable which lets you control the launch output but it has not been merged yet Add option to set output_format via environment variable by SammyRamone · Pull Request #713 · ros2/launch · GitHub. So in the future the following might not be necessary.)
ANSI control sequences allow you to manipulate the position of the cursor and how text is displayed. See ANSI escape code - Wikipedia for details on this. The short version is that we can use the character sequence “\x1b[” to initiate an ANSI control sequence. For example, we can use “\x1b[1k” to perform the “erase in line (EL)” command. Then we can do “\r” to move the cursor on the first position. Now we can just erase the launch tag infront of us. Now we get an output, which I find much easier to read.
We can also use this method to change how parts of the messages are displayed. This is especially interesting if we want to show additional information but still highlight the main message. For this we can use the ANSI “Select Graphic Rendition (SGR)” code. In this example, I made the message itself bold, the node name italic, and the additional information darker. Furthermore, the color based on the message type is only used for the message itself and not the additonal information. I added a dash infront of the message, which is helpful when using launch to differentiate messages from the nodes with messages from launch itself.
It is also possible to split one log into multiple lines. This leads to a clean look, but takes up more space.
By the way, you can also use these codes in the message itself. So if you want to send a message that blinks in the terminal, you can using “\x1b[6m”. Just beware that your message will look not so nice in rqt_consol or in a log file.
Here are some interesting codes
Newline: \n
Carriage return: \r
Delete everything until this point: \x1b[1k
Reset font to normal: \x1b[0m
Bold: \x1b[1m
Faint: \x1b[2m
Italic: \x1b[3m
Underline: \x1b[4m
Slow blink: \x1b[5m
Rapid blink: \x1b[6m
See also ANSI escape code - Wikipedia for more information and the color codes.
Some Tips:
- Beware when stetting a foreground color, as it will override the colors for the different log levels. However, if you only set the color after displaying the message itself (e.g. to put the name where it came from in a different color), then it will work fine.
- Don’t forget to reset any formatting changes with \x1b[0m or you might mess up any output that follows in the terminal
- Put the settings for logging in your .bashrc or .zshrc so that they are automatically set for each terminal.
If you have any further tips or ideas on how to get a nicer terminal output, I would be interested in hearing it.