24 #include "rcl/common.h"
25 #include "rcl/error_handling.h"
30 #include "rcl_interfaces/msg/log.h"
31 #include "rcutils/error_handling.h"
32 #include "rcutils/format_string.h"
33 #include "rcutils/logging.h"
34 #include "rcutils/logging_macros.h"
35 #include "rcutils/time.h"
36 #include "rcutils/types/char_array.h"
37 #include "rcutils/types/hash_map.h"
38 #include "rcutils/types/rcutils_ret.h"
39 #include "rosidl_runtime_c/string_functions.h"
41 #define ROSOUT_TOPIC_NAME "/rosout"
49 static rcutils_hash_map_t __logger_map;
50 static bool __is_initialized =
false;
61 static rcutils_hash_map_t __sublogger_map;
67 if (__is_initialized) {
70 __logger_map = rcutils_get_zero_initialized_hash_map();
71 status = rcl_convert_rcutils_ret_to_rcl_ret(
72 rcutils_hash_map_init(
74 rcutils_hash_map_string_hash_func, rcutils_hash_map_string_cmp_func, allocator));
79 __sublogger_map = rcutils_get_zero_initialized_hash_map();
80 status = rcl_convert_rcutils_ret_to_rcl_ret(
81 rcutils_hash_map_init(
83 rcutils_hash_map_string_hash_func, rcutils_hash_map_string_cmp_func, allocator));
85 rcl_ret_t fini_status = rcl_convert_rcutils_ret_to_rcl_ret(
86 rcutils_hash_map_fini(&__logger_map));
88 RCUTILS_SAFE_FWRITE_TO_STDERR(
"Failed to finalize the hash map for logger: ");
89 RCUTILS_SAFE_FWRITE_TO_STDERR(rcl_get_error_string().str);
91 RCUTILS_SAFE_FWRITE_TO_STDERR(
"\n");
96 __rosout_allocator = *allocator;
97 __is_initialized =
true;
103 _rcl_logging_rosout_remove_logger_map(
rcl_node_t * node)
108 char * previous_key = NULL;
111 rcutils_ret_t hashmap_ret = rcutils_hash_map_get_next_key_and_data(
112 &__logger_map, NULL, &key, &entry);
113 while (
RCL_RET_OK == status && RCUTILS_RET_OK == hashmap_ret) {
114 if (entry.node == node) {
115 status = rcl_convert_rcutils_ret_to_rcl_ret(rcutils_hash_map_unset(&__logger_map, &key));
121 hashmap_ret = rcutils_hash_map_get_next_key_and_data(
122 &__logger_map, previous_key ? &previous_key : NULL, &key, &entry);
129 _rcl_logging_rosout_clear_logger_map_item(
void * value)
136 status = rcl_convert_rcutils_ret_to_rcl_ret(
137 _rcl_logging_rosout_remove_logger_map(entry->node));
144 _rcl_logging_rosout_clear_sublogger_map_item(
void * value)
147 rcl_ret_t status = rcl_convert_rcutils_ret_to_rcl_ret(
148 rcutils_hash_map_unset(&__sublogger_map, &entry->name));
149 __rosout_allocator.deallocate(entry->name, __rosout_allocator.state);
150 __rosout_allocator.deallocate(entry->count, __rosout_allocator.state);
156 _rcl_logging_rosout_clear_hashmap(
157 rcutils_hash_map_t * map,
rcl_ret_t (* predicate)(
void *),
void * entry)
162 rcutils_ret_t hashmap_ret = rcutils_hash_map_get_next_key_and_data(
163 map, NULL, &key, entry);
164 while (RCUTILS_RET_OK == hashmap_ret) {
165 status = predicate(entry);
170 hashmap_ret = rcutils_hash_map_get_next_key_and_data(map, NULL, &key, entry);
172 if (RCUTILS_RET_HASH_MAP_NO_MORE_ENTRIES != hashmap_ret) {
173 status = rcl_convert_rcutils_ret_to_rcl_ret(hashmap_ret);
177 status = rcl_convert_rcutils_ret_to_rcl_ret(rcutils_hash_map_fini(map));
185 if (!__is_initialized) {
192 status = _rcl_logging_rosout_clear_hashmap(
193 &__logger_map, _rcl_logging_rosout_clear_logger_map_item, &entry);
198 status = _rcl_logging_rosout_clear_hashmap(
199 &__sublogger_map, _rcl_logging_rosout_clear_sublogger_map_item, &sublogger_entry);
204 __is_initialized =
false;
211 if (!__is_initialized) {
215 const char * logger_name = NULL;
222 if (NULL == logger_name) {
226 if (rcutils_hash_map_key_exists(&__logger_map, &logger_name)) {
230 if (NULL == node_name) {
231 node_name =
"unknown node";
234 RCUTILS_LOG_WARN_NAMED(
235 "rcl.logging_rosout",
236 "Publisher already registered for node name: '%s'. If this is due to multiple nodes "
237 "with the same name then all logs for the logger named '%s' will go out over "
238 "the existing publisher. As soon as any node with that name is destructed "
239 "it will unregister the publisher, preventing any further logs for that name from "
240 "being published on the rosout topic.",
247 const rosidl_message_type_support_t * type_support =
248 rosidl_typesupport_c__get_message_type_support_handle__rcl_interfaces__msg__Log();
253 RCL_CHECK_FOR_NULL_WITH_MSG(node_options,
"Node options was null.",
return RCL_RET_ERROR);
259 rcl_publisher_init(&new_entry.publisher, node, type_support, ROSOUT_TOPIC_NAME, &options);
263 new_entry.node = node;
264 status = rcl_convert_rcutils_ret_to_rcl_ret(
265 rcutils_hash_map_set(&__logger_map, &logger_name, &new_entry));
267 RCL_SET_ERROR_MSG(
"Failed to add publisher to map.");
271 RCL_UNUSED(fini_status);
280 if (!__is_initialized) {
285 const char * logger_name = NULL;
291 if (NULL == logger_name) {
294 if (!rcutils_hash_map_key_exists(&__logger_map, &logger_name)) {
299 status = rcl_convert_rcutils_ret_to_rcl_ret(
300 rcutils_hash_map_get(&__logger_map, &logger_name, &entry));
301 if (
RCL_RET_OK == status && node == entry.node) {
306 status = rcl_convert_rcutils_ret_to_rcl_ret(_rcl_logging_rosout_remove_logger_map(entry.node));
312 static void shallow_assign(rosidl_runtime_c__String * target,
const char * source)
314 target->data = (
char *)source;
315 size_t len = strlen(source);
317 target->capacity = len + 1;
321 const rcutils_log_location_t * location,
324 rcutils_time_point_value_t timestamp,
330 if (!__is_initialized) {
333 rcutils_ret_t rcutils_ret = rcutils_hash_map_get(&__logger_map, &name, &entry);
334 if (RCUTILS_RET_OK == rcutils_ret) {
335 char msg_buf[1024] =
"";
336 rcutils_char_array_t msg_array = {
338 .owns_buffer =
false,
340 .buffer_capacity =
sizeof(msg_buf),
341 .allocator = __rosout_allocator
344 status = rcl_convert_rcutils_ret_to_rcl_ret(
345 rcutils_char_array_vsprintf(&msg_array, format, *args));
347 RCUTILS_SAFE_FWRITE_TO_STDERR(
"Failed to format log string: ");
348 RCUTILS_SAFE_FWRITE_TO_STDERR(rcl_get_error_string().str);
350 RCUTILS_SAFE_FWRITE_TO_STDERR(
"\n");
352 rcl_interfaces__msg__Log log_message;
353 log_message.stamp.sec = (int32_t)
RCL_NS_TO_S(timestamp);
354 log_message.stamp.nanosec = (timestamp %
RCL_S_TO_NS(1));
355 log_message.level = severity;
356 log_message.line = (int32_t) location->line_number;
357 shallow_assign(&log_message.name, name);
358 shallow_assign(&log_message.msg, msg_array.buffer);
359 shallow_assign(&log_message.file, location->file_name);
360 shallow_assign(&log_message.function, location->function_name);
361 status =
rcl_publish(&entry.publisher, &log_message, NULL);
363 RCUTILS_SAFE_FWRITE_TO_STDERR(
"Failed to publish log message to rosout: ");
364 RCUTILS_SAFE_FWRITE_TO_STDERR(rcl_get_error_string().str);
366 RCUTILS_SAFE_FWRITE_TO_STDERR(
"\n");
370 status = rcl_convert_rcutils_ret_to_rcl_ret(rcutils_char_array_fini(&msg_array));
372 RCUTILS_SAFE_FWRITE_TO_STDERR(
"failed to fini char_array: ");
373 RCUTILS_SAFE_FWRITE_TO_STDERR(rcl_get_error_string().str);
375 RCUTILS_SAFE_FWRITE_TO_STDERR(
"\n");
381 _rcl_logging_rosout_get_full_sublogger_name(
382 const char * logger_name,
const char * sublogger_name,
char ** full_sublogger_name)
388 if (logger_name[0] ==
'\0' || sublogger_name[0] ==
'\0') {
389 RCL_SET_ERROR_MSG(
"logger name or sub-logger name can't be empty.");
393 *full_sublogger_name = rcutils_format_string(
394 __rosout_allocator,
"%s%s%s",
395 logger_name, RCUTILS_LOGGING_SEPARATOR_STRING, sublogger_name);
396 if (NULL == *full_sublogger_name) {
397 RCL_SET_ERROR_MSG(
"Failed to allocate a full sublogger name.");
406 const char * logger_name,
const char * sublogger_name)
408 if (!__is_initialized) {
413 char * full_sublogger_name = NULL;
414 uint64_t * sublogger_count = NULL;
420 rcutils_ret_t rcutils_ret = rcutils_hash_map_get(&__logger_map, &logger_name, &entry);
421 if (RCUTILS_RET_OK != rcutils_ret) {
422 if (RCUTILS_RET_NOT_FOUND == rcutils_ret) {
423 RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Failed to get logger entry for '%s'.", logger_name);
425 return rcl_convert_rcutils_ret_to_rcl_ret(rcutils_ret);
429 _rcl_logging_rosout_get_full_sublogger_name(logger_name, sublogger_name, &full_sublogger_name);
435 if (rcutils_hash_map_key_exists(&__logger_map, &full_sublogger_name)) {
437 status = rcl_convert_rcutils_ret_to_rcl_ret(
438 rcutils_hash_map_get(&__sublogger_map, &full_sublogger_name, &sublogger_entry));
440 RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
441 "Failed to get item from sublogger map for '%s'.", full_sublogger_name);
444 *sublogger_entry.count += 1;
448 status = rcl_convert_rcutils_ret_to_rcl_ret(
449 rcutils_hash_map_set(&__logger_map, &full_sublogger_name, &entry));
451 RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
452 "Failed to add publisher to map for logger '%s'.", full_sublogger_name);
456 sublogger_entry.name = full_sublogger_name;
457 sublogger_count = __rosout_allocator.allocate(
sizeof(uint64_t), __rosout_allocator.state);
458 if (!sublogger_count) {
460 "Failed to allocate memory for count of sublogger entry.");
463 sublogger_entry.count = sublogger_count;
464 *sublogger_entry.count = 1;
466 status = rcl_convert_rcutils_ret_to_rcl_ret(
467 rcutils_hash_map_set(&__sublogger_map, &full_sublogger_name, &sublogger_entry));
470 rcutils_ret_t rcutils_ret = rcutils_hash_map_unset(&__logger_map, &full_sublogger_name);
471 if (RCUTILS_RET_OK != rcutils_ret) {
472 RCUTILS_SAFE_FWRITE_TO_STDERR(
"failed to unset hashmap: ");
473 RCUTILS_SAFE_FWRITE_TO_STDERR(rcl_get_error_string().str);
475 RCUTILS_SAFE_FWRITE_TO_STDERR(
"\n");
483 __rosout_allocator.deallocate(sublogger_count, __rosout_allocator.state);
485 __rosout_allocator.deallocate(full_sublogger_name, __rosout_allocator.state);
491 const char * logger_name,
const char * sublogger_name)
493 if (!__is_initialized) {
498 char * full_sublogger_name = NULL;
504 _rcl_logging_rosout_get_full_sublogger_name(logger_name, sublogger_name, &full_sublogger_name);
510 if (!rcutils_hash_map_key_exists(&__logger_map, &full_sublogger_name)) {
512 RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Logger for '%s' not found.", full_sublogger_name);
517 status = rcl_convert_rcutils_ret_to_rcl_ret(
518 rcutils_hash_map_get(&__sublogger_map, &full_sublogger_name, &sublogger_entry));
520 RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
521 "Failed to get item from sublogger map for '%s'.", full_sublogger_name);
525 *sublogger_entry.count -= 1;
526 if (*sublogger_entry.count == 0) {
527 status = rcl_convert_rcutils_ret_to_rcl_ret(
528 rcutils_hash_map_unset(&__logger_map, &full_sublogger_name));
530 status = rcl_convert_rcutils_ret_to_rcl_ret(
531 rcutils_hash_map_unset(&__sublogger_map, &full_sublogger_name));
532 __rosout_allocator.deallocate(sublogger_entry.name, __rosout_allocator.state);
533 __rosout_allocator.deallocate(sublogger_entry.count, __rosout_allocator.state);
538 __rosout_allocator.deallocate(full_sublogger_name, __rosout_allocator.state);
rcutils_allocator_t rcl_allocator_t
Encapsulation of an allocator.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_logging_rosout_add_sublogger(const char *logger_name, const char *sublogger_name)
Add a subordinate logger based on a logger.
RCL_PUBLIC void rcl_logging_rosout_output_handler(const rcutils_log_location_t *location, int severity, const char *name, rcutils_time_point_value_t timestamp, const char *format, va_list *args)
The output handler outputs log messages to rosout topics.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_logging_rosout_init_publisher_for_node(rcl_node_t *node)
Creates a rosout publisher for a node and registers it to be used by the logging system.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_logging_rosout_fini_publisher_for_node(rcl_node_t *node)
Deregisters a rosout publisher for a node and cleans up allocated resources.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_logging_rosout_fini(void)
Uninitializes the rcl_logging_rosout features.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_logging_rosout_init(const rcl_allocator_t *allocator)
Initializes the rcl_logging_rosout features.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_logging_rosout_remove_sublogger(const char *logger_name, const char *sublogger_name)
Remove a subordinate logger and cleans up allocated resources.
RCL_PUBLIC RCL_WARN_UNUSED const rcl_node_options_t * rcl_node_get_options(const rcl_node_t *node)
Return the rcl node options.
RCL_PUBLIC RCL_WARN_UNUSED const char * rcl_node_get_name(const rcl_node_t *node)
Return the name of the node.
RCL_PUBLIC RCL_WARN_UNUSED const char * rcl_node_get_logger_name(const rcl_node_t *node)
Return the logger name of the node.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_publisher_init(rcl_publisher_t *publisher, const rcl_node_t *node, const rosidl_message_type_support_t *type_support, const char *topic_name, const rcl_publisher_options_t *options)
Initialize a rcl publisher.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_publish(const rcl_publisher_t *publisher, const void *ros_message, rmw_publisher_allocation_t *allocation)
Publish a ROS message on a topic using a publisher.
RCL_PUBLIC RCL_WARN_UNUSED rcl_publisher_options_t rcl_publisher_get_default_options(void)
Return the default publisher options in a rcl_publisher_options_t.
RCL_PUBLIC RCL_WARN_UNUSED rcl_publisher_t rcl_get_zero_initialized_publisher(void)
Return a rcl_publisher_t struct with members set to NULL.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_publisher_fini(rcl_publisher_t *publisher, rcl_node_t *node)
Finalize a rcl_publisher_t.
Structure which encapsulates the options for creating a rcl_node_t.
rmw_qos_profile_t rosout_qos
Middleware quality of service settings for /rosout.
rcl_allocator_t allocator
If true, no parameter infrastructure will be setup.
Structure which encapsulates a ROS Node.
Options available for a rcl publisher.
rmw_qos_profile_t qos
Middleware quality of service settings for the publisher.
rcl_allocator_t allocator
Custom allocator for the publisher, used for incidental allocations.
Structure which encapsulates a ROS Publisher.
#define RCL_NS_TO_S
Convenience macro to convert nanoseconds to seconds.
#define RCL_S_TO_NS
Convenience macro to convert seconds to nanoseconds.
#define RCL_RET_NOT_FOUND
Resource not found.
#define RCL_RET_OK
Success return code.
#define RCL_RET_BAD_ALLOC
Failed to allocate memory return code.
#define RCL_RET_INVALID_ARGUMENT
Invalid argument return code.
#define RCL_RET_ERROR
Unspecified error return code.
#define RCL_RET_NODE_INVALID
Invalid rcl_node_t given return code.
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.