diff --git a/rcl/include/rcl/arguments.h b/rcl/include/rcl/arguments.h index 44c99f6a9..d37363999 100644 --- a/rcl/include/rcl/arguments.h +++ b/rcl/include/rcl/arguments.h @@ -68,6 +68,9 @@ typedef struct rcl_arguments_s /// The ROS flag that precedes the ROS logging level to set. #define RCL_LOG_LEVEL_FLAG "--log-level" +/// The ROS log file name prefix to configure external logging. +#define RCL_EXTERNAL_LOG_FILE_NAME_PREFIX "--log-file-name" + /// The ROS flag that precedes the name of a configuration file to configure logging. #define RCL_EXTERNAL_LOG_CONFIG_FLAG "--log-config-file" diff --git a/rcl/src/rcl/arguments.c b/rcl/src/rcl/arguments.c index 85ac9d822..f094ecd8a 100644 --- a/rcl/src/rcl/arguments.c +++ b/rcl/src/rcl/arguments.c @@ -158,6 +158,22 @@ _rcl_parse_log_level( const char * arg, rcl_log_levels_t * log_levels); +/// Parse an argument that may or may not be a log file name prefix. +/** + * \param[in] arg the argument to parse + * \param[in] allocator an allocator to use + * \param[in,out] external log name file prefix + * \return RCL_RET_OK if a valid log file name prefix was parsed, or + * \return RCL_RET_BAD_ALLOC if an allocation failed, or + * \return RLC_RET_ERROR if an unspecified error occurred. + */ +RCL_LOCAL +rcl_ret_t +_rcl_parse_external_log_file_name( + const char * arg, + rcl_allocator_t allocator, + char ** log_file_name_prefix); + /// Parse an argument that may or may not be a log configuration file. /** * \param[in] arg the argument to parse @@ -436,6 +452,41 @@ rcl_parse_arguments( ROS_PACKAGE_NAME, "Arg %d (%s) is not a %s flag.", i, argv[i], RCL_LOG_LEVEL_FLAG); + // Attempt to parse argument as external log file name prefix + if (strcmp(RCL_EXTERNAL_LOG_FILE_NAME_PREFIX, argv[i]) == 0) { + if (i + 1 < argc) { + if (NULL != args_impl->external_log_file_name_prefix) { + RCUTILS_LOG_DEBUG_NAMED( + ROS_PACKAGE_NAME, "Overriding log file name : %s\n", + args_impl->external_log_file_name_prefix); + allocator.deallocate(args_impl->external_log_file_name_prefix, allocator.state); + args_impl->external_log_file_name_prefix = NULL; + } + if (RCL_RET_OK == _rcl_parse_external_log_file_name( + argv[i + 1], allocator, &args_impl->external_log_file_name_prefix)) + { + RCUTILS_LOG_DEBUG_NAMED( + ROS_PACKAGE_NAME, "Got log file name prefix : %s\n", + args_impl->external_log_file_name_prefix); + ++i; // Skip flag here, for loop will skip value. + continue; + } + rcl_error_string_t prev_error_string = rcl_get_error_string(); + rcl_reset_error(); + RCL_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Couldn't parse log file name prefix: '%s %s'. Error: %s", argv[i], argv[i + 1], + prev_error_string.str); + } else { + RCL_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Couldn't parse trailing %s flag. No string prefix provided.", argv[i]); + } + ret = RCL_RET_INVALID_ROS_ARGS; + goto fail; + } + RCUTILS_LOG_DEBUG_NAMED( + ROS_PACKAGE_NAME, "Arg %d (%s) is not a %s flag.", + i, argv[i], RCL_EXTERNAL_LOG_FILE_NAME_PREFIX); + // Attempt to parse argument as log configuration file if (strcmp(RCL_EXTERNAL_LOG_CONFIG_FLAG, argv[i]) == 0) { if (i + 1 < argc) { @@ -467,6 +518,9 @@ rcl_parse_arguments( ret = RCL_RET_INVALID_ROS_ARGS; goto fail; } + RCUTILS_LOG_DEBUG_NAMED( + ROS_PACKAGE_NAME, "Arg %d (%s) is not a %s flag.", + i, argv[i], RCL_EXTERNAL_LOG_CONFIG_FLAG); // Attempt to parse argument as a security enclave if (strcmp(RCL_ENCLAVE_FLAG, argv[i]) == 0 || strcmp(RCL_SHORT_ENCLAVE_FLAG, argv[i]) == 0) { @@ -499,10 +553,9 @@ rcl_parse_arguments( ret = RCL_RET_INVALID_ROS_ARGS; goto fail; } - RCUTILS_LOG_DEBUG_NAMED( ROS_PACKAGE_NAME, "Arg %d (%s) is not a %s flag.", - i, argv[i], RCL_EXTERNAL_LOG_CONFIG_FLAG); + i, argv[i], RCL_ENCLAVE_FLAG); // Attempt to parse --enable/disable-stdout-logs flag ret = _rcl_parse_disabling_flag( @@ -975,6 +1028,12 @@ rcl_arguments_fini( } args->impl->allocator.deallocate(args->impl->enclave, args->impl->allocator.state); + if (NULL != args->impl->external_log_file_name_prefix) { + args->impl->allocator.deallocate( + args->impl->external_log_file_name_prefix, args->impl->allocator.state); + args->impl->external_log_file_name_prefix = NULL; + } + if (NULL != args->impl->external_log_config_file) { args->impl->allocator.deallocate( args->impl->external_log_config_file, args->impl->allocator.state); @@ -1966,6 +2025,23 @@ _rcl_parse_param_file( return RCL_RET_OK; } +rcl_ret_t +_rcl_parse_external_log_file_name( + const char * arg, + rcl_allocator_t allocator, + char ** log_file_name_prefix) +{ + RCL_CHECK_ARGUMENT_FOR_NULL(arg, RCL_RET_INVALID_ARGUMENT); + RCL_CHECK_ARGUMENT_FOR_NULL(log_file_name_prefix, RCL_RET_INVALID_ARGUMENT); + + *log_file_name_prefix = rcutils_strdup(arg, allocator); + if (NULL == *log_file_name_prefix) { + RCL_SET_ERROR_MSG("Failed to allocate memory for external log file name prefix"); + return RCL_RET_BAD_ALLOC; + } + return RCL_RET_OK; +} + rcl_ret_t _rcl_parse_external_log_config_file( const char * arg, @@ -2048,6 +2124,7 @@ _rcl_allocate_initialized_arguments_impl(rcl_arguments_t * args, rcl_allocator_t args_impl->num_remap_rules = 0; args_impl->remap_rules = NULL; args_impl->log_levels = rcl_get_zero_initialized_log_levels(); + args_impl->external_log_file_name_prefix = NULL; args_impl->external_log_config_file = NULL; args_impl->unparsed_args = NULL; args_impl->num_unparsed_args = 0; diff --git a/rcl/src/rcl/arguments_impl.h b/rcl/src/rcl/arguments_impl.h index d3aedbb42..e8d73b585 100644 --- a/rcl/src/rcl/arguments_impl.h +++ b/rcl/src/rcl/arguments_impl.h @@ -53,6 +53,8 @@ struct rcl_arguments_impl_s /// Log levels parsed from arguments. rcl_log_levels_t log_levels; + /// A prefix used to external log file name + char * external_log_file_name_prefix; /// A file used to configure the external logging library char * external_log_config_file; /// A boolean value indicating if the standard out handler should be used for log output diff --git a/rcl/src/rcl/logging.c b/rcl/src/rcl/logging.c index 627247da0..26f19a0d3 100644 --- a/rcl/src/rcl/logging.c +++ b/rcl/src/rcl/logging.c @@ -66,6 +66,7 @@ rcl_logging_configure_with_output_handler( g_logging_allocator = *allocator; int default_level = -1; rcl_log_levels_t * log_levels = &global_args->impl->log_levels; + const char * file_name_prefix = global_args->impl->external_log_file_name_prefix; const char * config_file = global_args->impl->external_log_config_file; g_rcl_logging_stdout_enabled = !global_args->impl->log_stdout_disabled; g_rcl_logging_rosout_enabled = !global_args->impl->log_rosout_disabled; @@ -100,7 +101,8 @@ rcl_logging_configure_with_output_handler( } } if (g_rcl_logging_ext_lib_enabled) { - status = rcl_logging_external_initialize(config_file, g_logging_allocator); + status = rcl_logging_external_initialize( + file_name_prefix, config_file, g_logging_allocator); if (RCL_RET_OK == status) { rcl_logging_ret_t logging_status = rcl_logging_external_set_logger_level( NULL, default_level);