rcl icon indicating copy to clipboard operation
rcl copied to clipboard

use parent logger

Open iuhilnehc-ynos opened this issue 4 years ago • 14 comments

to fix https://github.com/ros2/rclcpp/issues/1694

Signed-off-by: Chen Lihui [email protected]

iuhilnehc-ynos avatar Jun 28 '21 09:06 iuhilnehc-ynos

I've got a couple of changes suggested inline.

Thanks for your review.

Separately, I'm slightly concerned about the run-time performance hit of this. That is, this function is called for every log message that goes out, and we are now making this function even more expensive for sub-loggers. I'm kind of wondering if we should instead add all sub-loggers to the __logger_map instead, which means we would use more memory, but keep this function faster. Thoughts?

You're right. It definitely costs more consumption each time while using the sub-loggers not existing. I think we could add all sub-loggers to the __logger_map. I'll reconsider it.

iuhilnehc-ynos avatar Jun 29 '21 02:06 iuhilnehc-ynos

@clalancette

I think a node is unnecessary to have multiple publishers on a topic named '/rosout', so I will have the publisher shared by the sub-logger.

The following is my thought.

typedef struct rosout_map_entry_t
{
  rcl_node_t * node;
  rcl_publisher_t publisher;
  // identicate whether the publisher is shared from parent node
  bool share_from_parent;
  // store the child logger names to make sure clean them if current node is finalized
  rcutils_array_list_t child_logger_names;
} rosout_map_entry_t;
  • rcl_logging_rosout_output_handler will create a new map item (with subloggername and its parent entry, the entry need to be updated) for a coming sublogger if necessary
  • also update rcl_logging_rosout_init_publisher_for_node and rcl_logging_rosout_fini_publisher_for_node

Do you have any suggestions?

iuhilnehc-ynos avatar Jun 29 '21 09:06 iuhilnehc-ynos

I think I understand your proposal; basically, whenever we see a new sublogger in rcl_logging_rosout_output_handler, we'll create a new entry in the map for it, and then subsequent calls to rcl_logging_rosout_output_handler can reuse that map entry. The rest of the changes are bookkeeping for cleaning up subloggers later on. Is that correct?

That will work, though it makes rcl_logging_rosout_output_handler more complicated and more jittery; it will "sometimes" have to do memory allocations in the fast path.

Before we go that route, let me ask a question: is the rosout logging layer notified when a new sublogger is created from a parent logger? If it isn't, can we make it so? Being able to do all of this setup work at sublogger creation time would be better, in my opinion.

clalancette avatar Jun 30 '21 15:06 clalancette

I think I understand your proposal; basically, whenever we see a new sublogger in rcl_logging_rosout_output_handler, we'll create a new entry in the map for it, and then subsequent calls to rcl_logging_rosout_output_handler can reuse that map entry. The rest of the changes are bookkeeping for cleaning up subloggers later on. Is that correct?

Yes.

That will work, though it makes rcl_logging_rosout_output_handler more complicated and more jittery; it will "sometimes" have to do memory allocations in the fast path.

Sorry for not finding out a better idea based on the current design. Maybe in the future, we could make an implicit node to create a publisher on the topic '/rosout'. (I don't know why ros2 needs to create multiple publishers on topic '/rosout' if there are multiple nodes.)

Before we go that route, let me ask a question: is the rosout logging layer notified when a new sublogger is created from a parent logger? If it isn't, can we make it so? Being able to do all of this setup work at sublogger creation time would be better, in my opinion.

Could you give me more explanations about is the rosout logging layer notified when a new sublogger is created from a parent logger? I don't know the purpose of this notification.

iuhilnehc-ynos avatar Jul 01 '21 03:07 iuhilnehc-ynos

Could you give me more explanations about is the rosout logging layer notified when a new sublogger is created from a parent logger? I don't know the purpose of this notification.

Going back to the original problem, it seems like the problem comes about in a line like this:

    RCLCPP_INFO(get_logger().get_child("child"), "Child logger");

Reading the rclcpp code a bit, it looks like get_logger() returns a Logger instance. And get_child() is a method on the Logger instance.

My suggestion is that we add a new function to https://github.com/ros2/rcl/blob/master/rcl/include/rcl/logging_rosout.h called something like rcl_add_sublogger(), which would allocate and add a sublogger to the __logger_map if it doesn't already exist. And then we would call that anytime we construct a new child logger (maybe just anytime we construct a Logger at all, I'm not sure). That way, we do the initialization at child logger construction time, and keep rcl_logging_rosout_output_handler simpler.

clalancette avatar Jul 01 '21 15:07 clalancette

Going back to the original problem, it seems like the problem comes about in a line like this:

    RCLCPP_INFO(get_logger().get_child("child"), "Child logger");

Reading the rclcpp code a bit, it looks like get_logger() returns a Logger instance. And get_child() is a method on the Logger instance.

My suggestion is that we add a new function to https://github.com/ros2/rcl/blob/master/rcl/include/rcl/logging_rosout.h called something like rcl_add_sublogger(), which would allocate and add a sublogger to the __logger_map if it doesn't already exist. And then we would call that anytime we construct a new child logger (maybe just anytime we construct a Logger at all, I'm not sure). That way, we do the initialization at child logger construction time, and keep rcl_logging_rosout_output_handler simpler.

Thank you for your suggestion. I'd really appreciate that. Do you mean to use the sublogger must through get_child()? If so, I think I thought of that implementation before, but it seems there is unexpected behaviour, if we can accept that, I'll continue to implement it.

there is a node named 'node',

  1. normal case
RCLCPP_INFO(get_logger().get_child("child"), "Child logger");

get_child() call rcl_add_sublogger to add a sublogger, so it will use the real sublogger to log message.

  1. expected case
RCLCPP_INFO(rclcpp::get_logger("node.child"), "not to publish message on rosout");

Because rcl_add_sublogger was not called before and there is no node named /node/child, it can't publish the message on rosout

  1. unexpected case

if get_child() was called before, such as

RCLCPP_INFO(get_logger().get_child("child"), "Child logger");

and then,

RCLCPP_INFO(rclcpp::get_logger("node.child"), "it will use the child logger");

Do you think the cases(2, 3) will make users confused?

iuhilnehc-ynos avatar Jul 02 '21 02:07 iuhilnehc-ynos

adding dynamic memory allocation and hashmap operation would be issue for logging as mentioned before. in production, logging is really important to make sure that we can have sufficient information w/o overhead.

Because rcl_add_sublogger was not called before

i think we can call internally based on "node.child"? if i am not mistaken.

fujitatomoya avatar Jul 02 '21 05:07 fujitatomoya

adding dynamic memory allocation and hashmap operation would be issue for logging as mentioned before. in production, logging is really important to make sure that we can have sufficient information w/o overhead.

Because rcl_add_sublogger was not called before

i think we can call internally based on "node.child"? if i am not mistaken.

Thank @fujitatomoya, if so, I think the added function rcl_add_sublogger only needs to be called in rcl instead of rclcpp. (In other words, rcl_add_sublogger can be a static function inside rcl, which will be called at rcl_logging_rosout_output_handler)

iuhilnehc-ynos avatar Jul 02 '21 05:07 iuhilnehc-ynos

@iuhilnehc-ynos Hm, good point about the "node.child" issue; I hadn't thought about that one.

In that case, I think I would propose:

  1. Create a function in rcl which is rcl_add_sublogger. You'll probably also want an rcl_remove_sublogger for destruction time.
  2. Call rcl_add_sublogger when a Logger instance is constructed (and rcl_remove_sublogger when it is destroyed).
  3. Also call rcl_add_sublogger from rcl_logger_rosout_output_handler if the logger isn't in the __logger_map.

That way, you can avoid allocations at runtime by doing:

auto sublogger = get_logger().get_child("child");
RCLCPP_INFO(sublogger, "foo");

But if you forget to do that and just do:

RCLCPP_INFO(get_logger("node.child"), "foo");

things will still work. What do you think about that?

clalancette avatar Jul 12 '21 17:07 clalancette

@clalancette

Thanks for your proposal. There is something that needs to be confirmed.

  1. Currently, the Logger of rcpcpp does not use rcl_node_t inside, which means we can't use the following interface in Logger,
rcl_ret_t
rcl_logging_rosout_add_sublogger(
  rcl_node_t * node, const char * sublogger_name)
{
  rcl_ret_t status = RCL_RET_OK;
  char * logger_name = NULL;

  RCL_LOGGING_ROSOUT_VERIFY_INITIALIZED
  RCL_CHECK_ARGUMENT_FOR_NULL(node, RCL_RET_NODE_INVALID);
  if (NULL == sublogger_name) {
    RCL_SET_ERROR_MSG("Sub-logger name was null.");
    return RCL_RET_ERROR;
  }

  logger_name = _rcl_logging_rosout_get_sublogger_name(node, sublogger_name);
  if (NULL == logger_name) {
    // Error already set
    return RCL_RET_ERROR;
  }

  status = _rcl_logging_rosout_add_logger(node, logger_name);
  // TODO: save logger_name into a list,
  // it can't be freed immediately because the hashmap need it

  return status;
}

so the rcl_logging_rosout_add_sublogger seems to be implemented as,

rcl_ret_t
rcl_logging_rosout_add_sublogger(
  const char * sublogger_name)
{
  1. to search from `__logger_map` by substring the `sublogger_name` to locate an existing rcl_node_t
  2. create a new string `sublogger_name_dup` from sublogger_name
  3. use the rcl_node_t to create a new publisher, and then add the item(sublogger_name_dup, new_entry) into `__logger_map`
  4. put the new `sublogger_name_dup` into a list
}

I don't think it's what we want because this kind of API (without rcl_node_t * argument) can't be the same group inside logging_rosout.c, I need to wait for your confirmation.

iuhilnehc-ynos avatar Jul 14 '21 06:07 iuhilnehc-ynos

1. Currently, the `Logger` of `rcpcpp` does not use `rcl_node_t` inside, which means we can't use the following interface in `Logger`,

Good point.

rcl_ret_t
rcl_logging_rosout_add_sublogger(
  const char * sublogger_name)
{
  1. to search from `__logger_map` by substring the `sublogger_name` to locate an existing rcl_node_t
  2. create a new string `sublogger_name_dup` from sublogger_name
  3. use the rcl_node_t to create a new publisher, and then add the item(sublogger_name_dup, new_entry) into `__logger_map`
  4. put the new `sublogger_name_dup` into a list
}

OK, I understand this pseudo code.

I don't think it's what we want because this kind of API (without rcl_node_t * argument) can't be the same group inside logging_rosout.c, I need to wait for your confirmation.

I don't quite understand this part. Can you rephrase?

clalancette avatar Jul 14 '21 13:07 clalancette

I don't think it's what we want because this kind of API (without rcl_node_t * argument) can't be the same group inside logging_rosout.c, I need to wait for your confirmation.

I don't quite understand this part. Can you rephrase?

Sorry to make you confused. I thought it seems like an indirect API in RCL because this API need to search a rcl_node_t inside. If only considering the implementation level of RCL, I'd prefer to encapsulate an API like the first one(rcl_logging_rosout_add_sublogger(rcl_node_t *, const char *)).

Anyway, I'll implement this API as

rcl_ret_t
rcl_logging_rosout_add_sublogger(
  const char * logger_name, const char * sublogger_name)

iuhilnehc-ynos avatar Jul 15 '21 01:07 iuhilnehc-ynos

@clalancette

Could you help review it in your spare time? Thanks in advance.

iuhilnehc-ynos avatar Jul 22 '21 05:07 iuhilnehc-ynos

@clalancette friendly ping.

fujitatomoya avatar Aug 25 '21 02:08 fujitatomoya

@clalancette friendly ping.

fujitatomoya avatar Feb 14 '23 18:02 fujitatomoya

@clalancette Thanks for your reviews. Updated in https://github.com/ros2/rcl/pull/921/commits/9f4005f5fdb10c2ad8cd1d3c2a54a8bf67bad83d.

iuhilnehc-ynos avatar Feb 16 '23 07:02 iuhilnehc-ynos

try CI:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • Windows Build Status

iuhilnehc-ynos avatar Feb 16 '23 09:02 iuhilnehc-ynos

CI:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • Windows Build Status

iuhilnehc-ynos avatar Feb 17 '23 03:02 iuhilnehc-ynos

CI:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • Windows Build Status

iuhilnehc-ynos avatar Feb 20 '23 04:02 iuhilnehc-ynos