Skip to content
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

Add rosconsole echo #1324

Merged
merged 7 commits into from
Feb 26, 2018
Merged

Conversation

efernandez
Copy link
Contributor

@efernandez efernandez commented Feb 11, 2018

This PR adds a new command to rosconsole that allows to print the logger messages from the /rosout or /rosout_agg topic using only the CLI.

This new command is echo, so we can simply call rosconsole echo to see all the logger messages. The command takes multiple optional arguments that modify its behaviour, as shown below:

$ rosconsole echo -h
usage: rosconsole echo [-h] [-1] [--topic TOPIC] [--nocolor] [-d]
                       [FILTER] [LEVEL]

Print logger messages

positional arguments:
  FILTER         regular expression to filter the logger name (default: .*)
  LEVEL          minimum logger level to print (default: warn)

optional arguments:
  -h, --help     show this help message and exit
  -1, --once     prints one logger message and exits
  --topic TOPIC  topic to read the logger messages from (default: /rosout)
  --nocolor      output without color
  -d, --detail   print full logger details

This video shows how it works using two test nodes from https://github.com/efernandez/rosconsole_echo_test:
https://youtu.be/Bn7f26OKBD8

The two positional arguments supports auto-complete hints thanks to ros/ros#168.

This is slightly inspired on @guillaumeautran 's rosdiagnostic tool. I tried to make it closer to how the other rosconsole commands are though, and also auto-complete friendly.

@efernandez
Copy link
Contributor Author

FYI @mikepurvis @dirk-thomas

Copy link
Member

@dirk-thomas dirk-thomas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CI failure is unrelated to this patch and has been addressed by #1325. Please rebase this branch to the latest commit of the targeted branch so that CI can pass.

from datetime import datetime
from dateutil.tz import tzlocal

from argparse import ArgumentParser
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems it would make sense to use argparse for the whole script. But I wouldn't couple that with the feature you are adding here. It is certainly good to use it for the new echo verb.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean I should make it local to the function where it's being used?

I honestly don't think it makes a big difference, but I can do that if that's what you're requesting here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, for not being clearer. I wasn't referring to the location of the import statement. Instead I was trying to point out that the first command line argument of rosconsole is inspected "manually" by looking at sys.argv and then argparse is only used for the new echo verb. It would be nice if argparse would be used for all arguments including the decision/selection based on the verb.

But that is clearly outside the scope of this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha! Yes, that makes a lot of sense, but definitely not a change for this PR.


parser.add_argument('--nocolor', action='store_true', help='output without color')

parser.add_argument('-d', '--detail', action='store_true', help='print full logger details')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Often such an option is called -v / --verbose.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.


rospy.spin()

del rosconsole
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleting the local variable doesn't do anything. Since the script is exited after this function call anywhere there is nothing which needs to be done. As an alternative - if the goal is to also be able to call this function without exiting right after it - the RosConsoleEcho class could have a deregister method which stops the subscription.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.


parser.add_argument('level', metavar='LEVEL', type=str, nargs='?', default='warn',
choices=RosConsoleEcho.STRING_LEVEL.keys(),
help='minimum logger level to print (default: %(default)s)')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since both positional arguments filter and level are optional it is not very clear how to pass e.g. only a level. Maybe consider to change either of them into -- option.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main reason I made them positional arguments is because I didn't know how to provide auto-complete hints with rosbash when I have something like -l (or --level).

I initially had --filter and --level. If I only have --level, do you if it's possible to have auto-complete hints in ros/ros#168?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is possible. Manually implemented completion is usually a 🤐 In newer code I usually rely on argcomplete but that would be a drastic change.

I will leave it up to you if you want to change the positional arguments to options.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've had a quick look at bash completion and it seems it's definitely possible. I don't know how easy and clean it'd be though.

Since I really like the idea of having --level, so we can change the level only, I'd give it a try.
I'll come back to you soon, after updating both PRs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to -l (--level) and update the rosbash auto-complete function to support it in ros/ros#168

self.LEVEL_STRING_COLOR

self._filter = re.compile(options.filter)
self._level = self.STRING_LEVEL[options.level.lower()]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could avoid the definition of STRING_LEVEL with the following code:

self._level = getattr(Log, options.level.upper())

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! Done.


def __init__(self, options):
self._level_string_map = self.LEVEL_STRING_NO_COLOR if options.nocolor else \
self.LEVEL_STRING_COLOR
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to reduce the two variables LEVEL_STRING_NO_COLOR and LEVEL_STRING_COLOR you could use the following more compact logic (just written in the browser):

LEVEL_COLOR = {
    'DEBUG': 92,
    'INFO': 97,
    'WARN': 93,
    'ERROR': 91,
    'FATAL': 95,
}
self._level_string_map = {getattr(Log, level): level.ljust(5) for level in LEVEL_COLOR.keys()}
if not options.nocolor:
    [
        self._level_string_map[getattr(Log, level)] = '\033[' + str(color) + 'm' + self._level_string_map[getattr(Log, level)] + '\033[0m'
        for level, color i LEVEL_COLOR.items()
    ]

In the (unlikely) event of another level being added in the future the lines to update would be much lower.

Copy link
Contributor Author

@efernandez efernandez Feb 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense.

I've done a different implementation though. Let me know if it looks good to you.

And some minor fixes related to the order of the levels when printed by argparse when the user introduces a wrong level.

rospy.Subscriber(options.topic, Log, callback)

def _stringify(self, level):
string = level.ljust(5)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dirk-thomas I feel I should change this to string = level.ljust(max([len(level) for level in LEVEL_COLOR.keys()]))

Sounds good?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Also rebased onto lunar-devel, although the tests were already passing before that.

@efernandez
Copy link
Contributor Author

Let me know if there is something else to do, or otherwise I'll squash all commits into one before you merge this.

@dirk-thomas
Copy link
Member

Thank you!

@dirk-thomas dirk-thomas merged commit 94aaaec into ros:lunar-devel Feb 26, 2018
dirk-thomas added a commit that referenced this pull request Apr 3, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants