18 #include "rcl/common.h"
19 #include "rcl/error_handling.h"
24 #include "rcl/visibility_control.h"
25 #include "rcl_interfaces/msg/log.h"
26 #include "rcutils/allocator.h"
27 #include "rcutils/format_string.h"
28 #include "rcutils/logging_macros.h"
29 #include "rcutils/macros.h"
30 #include "rcutils/types/hash_map.h"
31 #include "rcutils/types/rcutils_ret.h"
32 #include "rosidl_runtime_c/string_functions.h"
34 #define ROSOUT_TOPIC_NAME "/rosout"
42 static rcutils_hash_map_t __logger_map;
43 static bool __is_initialized =
false;
54 static rcutils_hash_map_t __sublogger_map;
60 if (__is_initialized) {
63 __logger_map = rcutils_get_zero_initialized_hash_map();
64 status = rcl_convert_rcutils_ret_to_rcl_ret(
65 rcutils_hash_map_init(
67 rcutils_hash_map_string_hash_func, rcutils_hash_map_string_cmp_func, allocator));
72 __sublogger_map = rcutils_get_zero_initialized_hash_map();
73 status = rcl_convert_rcutils_ret_to_rcl_ret(
74 rcutils_hash_map_init(
76 rcutils_hash_map_string_hash_func, rcutils_hash_map_string_cmp_func, allocator));
78 rcl_ret_t fini_status = rcl_convert_rcutils_ret_to_rcl_ret(
79 rcutils_hash_map_fini(&__logger_map));
81 RCUTILS_SAFE_FWRITE_TO_STDERR(
"Failed to finalize the hash map for logger: ");
82 RCUTILS_SAFE_FWRITE_TO_STDERR(rcl_get_error_string().str);
84 RCUTILS_SAFE_FWRITE_TO_STDERR(
"\n");
89 __rosout_allocator = *allocator;
90 __is_initialized =
true;
96 _rcl_logging_rosout_remove_logger_map(
rcl_node_t * node)
101 char * previous_key = NULL;
104 rcutils_ret_t hashmap_ret = rcutils_hash_map_get_next_key_and_data(
105 &__logger_map, NULL, &key, &entry);
106 while (
RCL_RET_OK == status && RCUTILS_RET_OK == hashmap_ret) {
107 if (entry.node == node) {
108 status = rcl_convert_rcutils_ret_to_rcl_ret(rcutils_hash_map_unset(&__logger_map, &key));
114 hashmap_ret = rcutils_hash_map_get_next_key_and_data(
115 &__logger_map, previous_key ? &previous_key : NULL, &key, &entry);
122 _rcl_logging_rosout_clear_logger_map_item(
void * value)
129 status = rcl_convert_rcutils_ret_to_rcl_ret(
130 _rcl_logging_rosout_remove_logger_map(entry->node));
137 _rcl_logging_rosout_clear_sublogger_map_item(
void * value)
140 rcl_ret_t status = rcl_convert_rcutils_ret_to_rcl_ret(
141 rcutils_hash_map_unset(&__sublogger_map, &entry->name));
142 __rosout_allocator.deallocate(entry->name, __rosout_allocator.state);
143 __rosout_allocator.deallocate(entry->count, __rosout_allocator.state);
149 _rcl_logging_rosout_clear_hashmap(
150 rcutils_hash_map_t * map,
rcl_ret_t (* predicate)(
void *),
void * entry)
155 rcutils_ret_t hashmap_ret = rcutils_hash_map_get_next_key_and_data(
156 map, NULL, &key, entry);
157 while (RCUTILS_RET_OK == hashmap_ret) {
158 status = predicate(entry);
163 hashmap_ret = rcutils_hash_map_get_next_key_and_data(map, NULL, &key, entry);
165 if (RCUTILS_RET_HASH_MAP_NO_MORE_ENTRIES != hashmap_ret) {
166 status = rcl_convert_rcutils_ret_to_rcl_ret(hashmap_ret);
170 status = rcl_convert_rcutils_ret_to_rcl_ret(rcutils_hash_map_fini(map));
178 if (!__is_initialized) {
185 status = _rcl_logging_rosout_clear_hashmap(
186 &__logger_map, _rcl_logging_rosout_clear_logger_map_item, &entry);
191 status = _rcl_logging_rosout_clear_hashmap(
192 &__sublogger_map, _rcl_logging_rosout_clear_sublogger_map_item, &sublogger_entry);
197 __is_initialized =
false;
204 if (!__is_initialized) {
208 const char * logger_name = NULL;
215 if (NULL == logger_name) {
219 if (rcutils_hash_map_key_exists(&__logger_map, &logger_name)) {
223 if (NULL == node_name) {
224 node_name =
"unknown node";
227 RCUTILS_LOG_WARN_NAMED(
228 "rcl.logging_rosout",
229 "Publisher already registered for node name: '%s'. If this is due to multiple nodes "
230 "with the same name then all logs for the logger named '%s' will go out over "
231 "the existing publisher. As soon as any node with that name is destructed "
232 "it will unregister the publisher, preventing any further logs for that name from "
233 "being published on the rosout topic.",
240 const rosidl_message_type_support_t * type_support =
241 rosidl_typesupport_c__get_message_type_support_handle__rcl_interfaces__msg__Log();
246 RCL_CHECK_FOR_NULL_WITH_MSG(node_options,
"Node options was null.",
return RCL_RET_ERROR);
252 rcl_publisher_init(&new_entry.publisher, node, type_support, ROSOUT_TOPIC_NAME, &options);
256 new_entry.node = node;
257 status = rcl_convert_rcutils_ret_to_rcl_ret(
258 rcutils_hash_map_set(&__logger_map, &logger_name, &new_entry));
260 RCL_SET_ERROR_MSG(
"Failed to add publisher to map.");
264 RCL_UNUSED(fini_status);
273 if (!__is_initialized) {
278 const char * logger_name = NULL;
284 if (NULL == logger_name) {
287 if (!rcutils_hash_map_key_exists(&__logger_map, &logger_name)) {
292 status = rcl_convert_rcutils_ret_to_rcl_ret(
293 rcutils_hash_map_get(&__logger_map, &logger_name, &entry));
294 if (
RCL_RET_OK == status && node == entry.node) {
299 status = rcl_convert_rcutils_ret_to_rcl_ret(_rcl_logging_rosout_remove_logger_map(entry.node));
305 static void shallow_assign(rosidl_runtime_c__String * target,
const char * source)
307 target->data = (
char *)source;
308 size_t len = strlen(source);
310 target->capacity = len + 1;
314 const rcutils_log_location_t * location,
317 rcutils_time_point_value_t timestamp,
323 if (!__is_initialized) {
326 rcutils_ret_t rcutils_ret = rcutils_hash_map_get(&__logger_map, &name, &entry);
327 if (RCUTILS_RET_OK == rcutils_ret) {
328 char msg_buf[1024] =
"";
329 rcutils_char_array_t msg_array = {
331 .owns_buffer =
false,
333 .buffer_capacity =
sizeof(msg_buf),
334 .allocator = __rosout_allocator
337 status = rcl_convert_rcutils_ret_to_rcl_ret(
338 rcutils_char_array_vsprintf(&msg_array, format, *args));
340 RCUTILS_SAFE_FWRITE_TO_STDERR(
"Failed to format log string: ");
341 RCUTILS_SAFE_FWRITE_TO_STDERR(rcl_get_error_string().str);
343 RCUTILS_SAFE_FWRITE_TO_STDERR(
"\n");
345 rcl_interfaces__msg__Log log_message;
346 log_message.stamp.sec = (int32_t)
RCL_NS_TO_S(timestamp);
347 log_message.stamp.nanosec = (timestamp %
RCL_S_TO_NS(1));
348 log_message.level = severity;
349 log_message.line = (int32_t) location->line_number;
350 shallow_assign(&log_message.name, name);
351 shallow_assign(&log_message.msg, msg_array.buffer);
352 shallow_assign(&log_message.file, location->file_name);
353 shallow_assign(&log_message.function, location->function_name);
354 status =
rcl_publish(&entry.publisher, &log_message, NULL);
356 RCUTILS_SAFE_FWRITE_TO_STDERR(
"Failed to publish log message to rosout: ");
357 RCUTILS_SAFE_FWRITE_TO_STDERR(rcl_get_error_string().str);
359 RCUTILS_SAFE_FWRITE_TO_STDERR(
"\n");
363 status = rcl_convert_rcutils_ret_to_rcl_ret(rcutils_char_array_fini(&msg_array));
365 RCUTILS_SAFE_FWRITE_TO_STDERR(
"failed to fini char_array: ");
366 RCUTILS_SAFE_FWRITE_TO_STDERR(rcl_get_error_string().str);
368 RCUTILS_SAFE_FWRITE_TO_STDERR(
"\n");
374 _rcl_logging_rosout_get_full_sublogger_name(
375 const char * logger_name,
const char * sublogger_name,
char ** full_sublogger_name)
381 if (logger_name[0] ==
'\0' || sublogger_name[0] ==
'\0') {
382 RCL_SET_ERROR_MSG(
"logger name or sub-logger name can't be empty.");
386 *full_sublogger_name = rcutils_format_string(
387 __rosout_allocator,
"%s%s%s",
388 logger_name, RCUTILS_LOGGING_SEPARATOR_STRING, sublogger_name);
389 if (NULL == *full_sublogger_name) {
390 RCL_SET_ERROR_MSG(
"Failed to allocate a full sublogger name.");
399 const char * logger_name,
const char * sublogger_name)
401 if (!__is_initialized) {
406 char * full_sublogger_name = NULL;
407 uint64_t * sublogger_count = NULL;
413 rcutils_ret_t rcutils_ret = rcutils_hash_map_get(&__logger_map, &logger_name, &entry);
414 if (RCUTILS_RET_OK != rcutils_ret) {
415 if (RCUTILS_RET_NOT_FOUND == rcutils_ret) {
416 RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Failed to get logger entry for '%s'.", logger_name);
418 return rcl_convert_rcutils_ret_to_rcl_ret(rcutils_ret);
422 _rcl_logging_rosout_get_full_sublogger_name(logger_name, sublogger_name, &full_sublogger_name);
428 if (rcutils_hash_map_key_exists(&__logger_map, &full_sublogger_name)) {
430 status = rcl_convert_rcutils_ret_to_rcl_ret(
431 rcutils_hash_map_get(&__sublogger_map, &full_sublogger_name, &sublogger_entry));
433 RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
434 "Failed to get item from sublogger map for '%s'.", full_sublogger_name);
437 *sublogger_entry.count += 1;
441 status = rcl_convert_rcutils_ret_to_rcl_ret(
442 rcutils_hash_map_set(&__logger_map, &full_sublogger_name, &entry));
444 RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
445 "Failed to add publisher to map for logger '%s'.", full_sublogger_name);
449 sublogger_entry.name = full_sublogger_name;
450 sublogger_count = __rosout_allocator.allocate(
sizeof(uint64_t), __rosout_allocator.state);
451 if (!sublogger_count) {
453 "Failed to allocate memory for count of sublogger entry.");
456 sublogger_entry.count = sublogger_count;
457 *sublogger_entry.count = 1;
459 status = rcl_convert_rcutils_ret_to_rcl_ret(
460 rcutils_hash_map_set(&__sublogger_map, &full_sublogger_name, &sublogger_entry));
463 rcutils_ret_t rcutils_ret = rcutils_hash_map_unset(&__logger_map, &full_sublogger_name);
464 if (RCUTILS_RET_OK != rcutils_ret) {
465 RCUTILS_SAFE_FWRITE_TO_STDERR(
"failed to unset hashmap: ");
466 RCUTILS_SAFE_FWRITE_TO_STDERR(rcl_get_error_string().str);
468 RCUTILS_SAFE_FWRITE_TO_STDERR(
"\n");
476 __rosout_allocator.deallocate(sublogger_count, __rosout_allocator.state);
478 __rosout_allocator.deallocate(full_sublogger_name, __rosout_allocator.state);
484 const char * logger_name,
const char * sublogger_name)
486 if (!__is_initialized) {
491 char * full_sublogger_name = NULL;
497 _rcl_logging_rosout_get_full_sublogger_name(logger_name, sublogger_name, &full_sublogger_name);
503 if (!rcutils_hash_map_key_exists(&__logger_map, &full_sublogger_name)) {
505 RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Logger for '%s' not found.", full_sublogger_name);
510 status = rcl_convert_rcutils_ret_to_rcl_ret(
511 rcutils_hash_map_get(&__sublogger_map, &full_sublogger_name, &sublogger_entry));
513 RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
514 "Failed to get item from sublogger map for '%s'.", full_sublogger_name);
518 *sublogger_entry.count -= 1;
519 if (*sublogger_entry.count == 0) {
520 status = rcl_convert_rcutils_ret_to_rcl_ret(
521 rcutils_hash_map_unset(&__logger_map, &full_sublogger_name));
523 status = rcl_convert_rcutils_ret_to_rcl_ret(
524 rcutils_hash_map_unset(&__sublogger_map, &full_sublogger_name));
525 __rosout_allocator.deallocate(sublogger_entry.name, __rosout_allocator.state);
526 __rosout_allocator.deallocate(sublogger_entry.count, __rosout_allocator.state);
531 __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.