From e2456ba634223eb64aec197da7923295b468df6b Mon Sep 17 00:00:00 2001 From: Christophe Bedard Date: Tue, 13 Oct 2020 17:03:47 -0400 Subject: [PATCH] Allow configuring logging directory through environment variables (#53) Signed-off-by: Christophe Bedard --- rcl_logging_interface/CMakeLists.txt | 34 +++- .../rcl_logging_interface.h | 25 +++ rcl_logging_interface/package.xml | 4 + rcl_logging_interface/src/logging_dir.c | 107 ++++++++++ .../test/test_get_logging_directory.cpp | 183 ++++++++++++++++++ .../rcl_logging_log4cxx.cpp | 19 +- rcl_logging_spdlog/CMakeLists.txt | 3 +- rcl_logging_spdlog/package.xml | 2 +- rcl_logging_spdlog/src/rcl_logging_spdlog.cpp | 51 ++--- 9 files changed, 380 insertions(+), 48 deletions(-) create mode 100644 rcl_logging_interface/src/logging_dir.c create mode 100644 rcl_logging_interface/test/test_get_logging_directory.cpp diff --git a/rcl_logging_interface/CMakeLists.txt b/rcl_logging_interface/CMakeLists.txt index 1dd9204..b1002a6 100644 --- a/rcl_logging_interface/CMakeLists.txt +++ b/rcl_logging_interface/CMakeLists.txt @@ -18,21 +18,41 @@ endif() find_package(ament_cmake_ros REQUIRED) find_package(rcutils REQUIRED) -add_library(${PROJECT_NAME} INTERFACE) -target_include_directories(${PROJECT_NAME} INTERFACE +set(${PROJECT_NAME}_sources + "src/logging_dir.c" +) +add_library(${PROJECT_NAME} ${${PROJECT_NAME}_sources}) +target_include_directories(${PROJECT_NAME} PUBLIC + "$" "$") -ament_target_dependencies(${PROJECT_NAME} INTERFACE rcutils) +ament_target_dependencies(${PROJECT_NAME} rcutils) +target_compile_definitions(${PROJECT_NAME} PRIVATE "RCL_LOGGING_INTERFACE_BUILDING_DLL") +install( + DIRECTORY include/ + DESTINATION include +) install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin) -install(DIRECTORY include/${PROJECT_NAME}/ - DESTINATION include/${PROJECT_NAME} -) - ament_export_include_directories(include) ament_export_targets(${PROJECT_NAME}) ament_export_dependencies(rcutils) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() + + find_package(ament_cmake_gtest REQUIRED) + find_package(rcpputils REQUIRED) + ament_add_gtest(test_get_logging_directory test/test_get_logging_directory.cpp) + if(TARGET test_get_logging_directory) + target_link_libraries(test_get_logging_directory ${PROJECT_NAME}) + target_include_directories(test_get_logging_directory PRIVATE include) + ament_target_dependencies(test_get_logging_directory rcutils rcpputils) + endif() +endif() + ament_package() diff --git a/rcl_logging_interface/include/rcl_logging_interface/rcl_logging_interface.h b/rcl_logging_interface/include/rcl_logging_interface/rcl_logging_interface.h index 235942f..074f86a 100644 --- a/rcl_logging_interface/include/rcl_logging_interface/rcl_logging_interface.h +++ b/rcl_logging_interface/include/rcl_logging_interface/rcl_logging_interface.h @@ -26,6 +26,7 @@ typedef enum { RCL_LOGGING_RET_OK = 0, RCL_LOGGING_RET_ERROR = 2, + RCL_LOGGING_RET_INVALID_ARGUMENT = 11, RCL_LOGGING_RET_CONFIG_FILE_DOESNT_EXIST = 21, RCL_LOGGING_RET_CONFIG_FILE_INVALID = 22, } rcl_logging_ret_t; @@ -91,6 +92,30 @@ RCL_LOGGING_INTERFACE_PUBLIC RCUTILS_WARN_UNUSED rcl_logging_ret_t rcl_logging_external_set_logger_level(const char * name, int level); +/// Get the logging directory. +/** + * Uses various environment variables to construct a logging directory path. + * + * Use $ROS_LOG_DIR if ROS_LOG_DIR is set and not empty. + * Otherwise, use $ROS_HOME/log, using ~/.ros for ROS_HOME if not set or if empty. + * + * It also expands an initial '~' to the current user's home directory, + * and converts the path separator if necessary. + * + * If successful, the directory C string should be deallocated using the given allocator when it is + * no longer needed. + * + * \param[in] allocator The allocator to use for memory allocation. + * \param[out] directory The C string pointer at which to write the directory path. + * Only meaningful if the call is successful. Must not be nullptr and must point to nullptr. + * \return RCL_LOGGING_RET_OK if successful, or + * \return RCL_LOGGING_RET_INVALID_ARGUMENT if any arguments are invalid, or + * \return RCL_LOGGING_RET_ERROR if an unspecified error occurs. + */ +RCL_LOGGING_INTERFACE_PUBLIC +rcl_logging_ret_t +rcl_logging_get_logging_directory(rcutils_allocator_t allocator, char ** directory); + #ifdef __cplusplus } #endif diff --git a/rcl_logging_interface/package.xml b/rcl_logging_interface/package.xml index e0f9a2d..6c68c20 100644 --- a/rcl_logging_interface/package.xml +++ b/rcl_logging_interface/package.xml @@ -13,6 +13,10 @@ rcutils + ament_lint_auto + ament_lint_common + rcpputils + ament_cmake diff --git a/rcl_logging_interface/src/logging_dir.c b/rcl_logging_interface/src/logging_dir.c new file mode 100644 index 0000000..23d0f03 --- /dev/null +++ b/rcl_logging_interface/src/logging_dir.c @@ -0,0 +1,107 @@ +// Copyright 2020 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include + +#include "rcl_logging_interface/rcl_logging_interface.h" + +rcl_logging_ret_t +rcl_logging_get_logging_directory(rcutils_allocator_t allocator, char ** directory) +{ + if (NULL == directory) { + RCUTILS_SET_ERROR_MSG("directory argument must not be null"); + return RCL_LOGGING_RET_INVALID_ARGUMENT; + } + if (NULL != *directory) { + RCUTILS_SET_ERROR_MSG("directory argument must point to null"); + return RCL_LOGGING_RET_INVALID_ARGUMENT; + } + + const char * log_dir_env; + const char * err = rcutils_get_env("ROS_LOG_DIR", &log_dir_env); + if (NULL != err) { + RCUTILS_SET_ERROR_MSG("rcutils_get_env failed"); + return RCL_LOGGING_RET_ERROR; + } + if ('\0' != *log_dir_env) { + *directory = rcutils_strdup(log_dir_env, allocator); + if (NULL == *directory) { + RCUTILS_SET_ERROR_MSG("rcutils_strdup failed"); + return RCL_LOGGING_RET_ERROR; + } + } else { + const char * ros_home_dir_env; + err = rcutils_get_env("ROS_HOME", &ros_home_dir_env); + if (NULL != err) { + RCUTILS_SET_ERROR_MSG("rcutils_get_env failed"); + return RCL_LOGGING_RET_ERROR; + } + char * ros_home_dir; + if ('\0' == *ros_home_dir_env) { + ros_home_dir = rcutils_join_path("~", ".ros", allocator); + if (NULL == ros_home_dir) { + RCUTILS_SET_ERROR_MSG("rcutils_join_path failed"); + return RCL_LOGGING_RET_ERROR; + } + } else { + ros_home_dir = rcutils_strdup(ros_home_dir_env, allocator); + if (NULL == ros_home_dir) { + RCUTILS_SET_ERROR_MSG("rcutils_strdup failed"); + return RCL_LOGGING_RET_ERROR; + } + } + *directory = rcutils_join_path(ros_home_dir, "log", allocator); + allocator.deallocate(ros_home_dir, allocator.state); + if (NULL == *directory) { + RCUTILS_SET_ERROR_MSG("rcutils_join_path failed"); + return RCL_LOGGING_RET_ERROR; + } + } + + // Expand home directory + if ('~' == (*directory)[0]) { + const char * homedir = rcutils_get_home_dir(); + if (NULL == homedir) { + allocator.deallocate(*directory, allocator.state); + RCUTILS_SET_ERROR_MSG("failed to get the home directory"); + return RCL_LOGGING_RET_ERROR; + } + char * directory_not_expanded = *directory; + *directory = rcutils_format_string_limit( + allocator, + strlen(homedir) + strlen(directory_not_expanded), + "%s%s", + homedir, + directory_not_expanded + 1); + allocator.deallocate(directory_not_expanded, allocator.state); + if (NULL == *directory) { + RCUTILS_SET_ERROR_MSG("rcutils_format_string failed"); + return RCL_LOGGING_RET_ERROR; + } + } + + char * directory_maybe_not_native = *directory; + *directory = rcutils_to_native_path(directory_maybe_not_native, allocator); + allocator.deallocate(directory_maybe_not_native, allocator.state); + if (NULL == *directory) { + RCUTILS_SET_ERROR_MSG("rcutils_to_native_path failed"); + return RCL_LOGGING_RET_ERROR; + } + return RCL_LOGGING_RET_OK; +} diff --git a/rcl_logging_interface/test/test_get_logging_directory.cpp b/rcl_logging_interface/test/test_get_logging_directory.cpp new file mode 100644 index 0000000..2c9ed8e --- /dev/null +++ b/rcl_logging_interface/test/test_get_logging_directory.cpp @@ -0,0 +1,183 @@ +// Copyright 2020 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include +#include + +#include "gtest/gtest.h" +#include "rcl_logging_interface/rcl_logging_interface.h" + +// This is a helper class that resets an environment +// variable when leaving scope +class RestoreEnvVar +{ +public: + explicit RestoreEnvVar(const std::string & name) + : name_(name), + value_(rcpputils::get_env_var(name.c_str())) + { + } + + ~RestoreEnvVar() + { + if (!rcutils_set_env(name_.c_str(), value_.c_str())) { + std::cerr << "Failed to restore value of environment variable: " << name_ << std::endl; + } + } + +private: + const std::string name_; + const std::string value_; +}; + +TEST(test_logging_directory, directory) +{ + RestoreEnvVar home_var("HOME"); + RestoreEnvVar userprofile_var("USERPROFILE"); + ASSERT_EQ(true, rcutils_set_env("HOME", nullptr)); + ASSERT_EQ(true, rcutils_set_env("USERPROFILE", nullptr)); + ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", nullptr)); + ASSERT_EQ(true, rcutils_set_env("ROS_HOME", nullptr)); + + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + + // Invalid argument if given a nullptr + EXPECT_EQ( + RCL_LOGGING_RET_INVALID_ARGUMENT, rcl_logging_get_logging_directory(allocator, nullptr)); + EXPECT_TRUE(rcutils_error_is_set()); + rcutils_reset_error(); + // Invalid argument if the C string is not nullptr + char * could_leak = const_cast("/could/be/leaked"); + EXPECT_EQ( + RCL_LOGGING_RET_INVALID_ARGUMENT, rcl_logging_get_logging_directory(allocator, &could_leak)); + EXPECT_TRUE(rcutils_error_is_set()); + rcutils_reset_error(); + + // Fails without any relevant env vars at all (HOME included) + char * directory = nullptr; + EXPECT_EQ(RCL_LOGGING_RET_ERROR, rcl_logging_get_logging_directory(allocator, &directory)); + EXPECT_TRUE(rcutils_error_is_set()); + rcutils_reset_error(); + directory = nullptr; + + // Default case without ROS_LOG_DIR or ROS_HOME being set (but with HOME) + rcpputils::fs::path fake_home("/fake_home_dir"); + ASSERT_EQ(true, rcutils_set_env("HOME", fake_home.string().c_str())); + ASSERT_EQ(true, rcutils_set_env("USERPROFILE", fake_home.string().c_str())); + rcpputils::fs::path default_dir = fake_home / ".ros" / "log"; + EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); + EXPECT_STREQ(directory, default_dir.string().c_str()); + allocator.deallocate(directory, allocator.state); + directory = nullptr; + + // Use $ROS_LOG_DIR if it is set + const char * my_log_dir_raw = "/my/ros_log_dir"; + rcpputils::fs::path my_log_dir(my_log_dir_raw); + ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", my_log_dir.string().c_str())); + EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); + EXPECT_STREQ(directory, my_log_dir.string().c_str()); + allocator.deallocate(directory, allocator.state); + directory = nullptr; + // Make sure it converts path separators when necessary + ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", my_log_dir_raw)); + EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); + EXPECT_STREQ(directory, my_log_dir.string().c_str()); + allocator.deallocate(directory, allocator.state); + directory = nullptr; + // Setting ROS_HOME won't change anything since ROS_LOG_DIR is used first + ASSERT_EQ(true, rcutils_set_env("ROS_HOME", "/this/wont/be/used")); + EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); + EXPECT_STREQ(directory, my_log_dir.string().c_str()); + allocator.deallocate(directory, allocator.state); + directory = nullptr; + ASSERT_EQ(true, rcutils_set_env("ROS_HOME", nullptr)); + // Empty is considered unset + ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", "")); + EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); + EXPECT_STREQ(directory, default_dir.string().c_str()); + allocator.deallocate(directory, allocator.state); + directory = nullptr; + // Make sure '~' is expanded to the home directory + ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", "~/logdir")); + EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); + rcpputils::fs::path fake_log_dir = fake_home / "logdir"; + EXPECT_STREQ(directory, fake_log_dir.string().c_str()); + allocator.deallocate(directory, allocator.state); + directory = nullptr; + // But it should only be expanded if it's at the beginning + rcpputils::fs::path prefixed_fake_log_dir("/prefix/~/logdir"); + ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", prefixed_fake_log_dir.string().c_str())); + EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); + EXPECT_STREQ(directory, prefixed_fake_log_dir.string().c_str()); + allocator.deallocate(directory, allocator.state); + directory = nullptr; + ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", "~")); + EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); + EXPECT_STREQ(directory, fake_home.string().c_str()); + allocator.deallocate(directory, allocator.state); + directory = nullptr; + rcpputils::fs::path home_trailing_slash(fake_home.string() + "/"); + ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", "~/")); + EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); + EXPECT_STREQ(directory, home_trailing_slash.string().c_str()); + allocator.deallocate(directory, allocator.state); + directory = nullptr; + + ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", nullptr)); + + // Without ROS_LOG_DIR, use $ROS_HOME/log + rcpputils::fs::path fake_ros_home = fake_home / ".fakeroshome"; + ASSERT_EQ(true, rcutils_set_env("ROS_HOME", fake_ros_home.string().c_str())); + EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); + rcpputils::fs::path fake_ros_home_log_dir = fake_ros_home / "log"; + EXPECT_STREQ(directory, fake_ros_home_log_dir.string().c_str()); + allocator.deallocate(directory, allocator.state); + directory = nullptr; + // Make sure it converts path separators when necessary + const char * my_ros_home_raw = "/my/ros/home"; + ASSERT_EQ(true, rcutils_set_env("ROS_HOME", my_ros_home_raw)); + EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); + rcpputils::fs::path my_ros_home_log_dir = rcpputils::fs::path(my_ros_home_raw) / "log"; + EXPECT_STREQ(directory, my_ros_home_log_dir.string().c_str()); + allocator.deallocate(directory, allocator.state); + directory = nullptr; + // Empty is considered unset + ASSERT_EQ(true, rcutils_set_env("ROS_HOME", "")); + EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); + EXPECT_STREQ(directory, default_dir.string().c_str()); + allocator.deallocate(directory, allocator.state); + directory = nullptr; + // Make sure '~' is expanded to the home directory + ASSERT_EQ(true, rcutils_set_env("ROS_HOME", "~/.fakeroshome")); + EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); + EXPECT_STREQ(directory, fake_ros_home_log_dir.string().c_str()); + allocator.deallocate(directory, allocator.state); + directory = nullptr; + // But it should only be expanded if it's at the beginning + rcpputils::fs::path prefixed_fake_ros_home("/prefix/~/.fakeroshome"); + rcpputils::fs::path prefixed_fake_ros_home_log_dir = prefixed_fake_ros_home / "log"; + ASSERT_EQ(true, rcutils_set_env("ROS_HOME", prefixed_fake_ros_home.string().c_str())); + EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); + EXPECT_STREQ(directory, prefixed_fake_ros_home_log_dir.string().c_str()); + allocator.deallocate(directory, allocator.state); + directory = nullptr; + + ASSERT_EQ(true, rcutils_set_env("ROS_HOME", nullptr)); +} diff --git a/rcl_logging_log4cxx/src/rcl_logging_log4cxx/rcl_logging_log4cxx.cpp b/rcl_logging_log4cxx/src/rcl_logging_log4cxx/rcl_logging_log4cxx.cpp index 3adf80c..11a8afb 100644 --- a/rcl_logging_log4cxx/src/rcl_logging_log4cxx/rcl_logging_log4cxx.cpp +++ b/rcl_logging_log4cxx/src/rcl_logging_log4cxx/rcl_logging_log4cxx.cpp @@ -102,19 +102,20 @@ rcl_logging_ret_t rcl_logging_external_initialize( // To be compatible with ROS 1, we want to construct a default filename of // the form ~/.ros/log/__.log - // First get the home directory. - const char * homedir = rcutils_get_home_dir(); - if (homedir == nullptr) { - // We couldn't get the home directory; it is not really going to be - // possible to do logging properly, so get out of here without setting - // up logging. - return RCL_LOGGING_RET_ERROR; + char * logdir = nullptr; + rcl_logging_ret_t dir_ret = rcl_logging_get_logging_directory(allocator, &logdir); + if (RCL_LOGGING_RET_OK != dir_ret) { + // We couldn't get the log directory, so get out of here without setting up + // logging. + RCUTILS_SET_ERROR_MSG("Failed to get logging directory"); + return dir_ret; } // Now get the milliseconds since the epoch in the local timezone. rcutils_time_point_value_t now; rcutils_ret_t ret = rcutils_system_time_now(&now); if (ret != RCUTILS_RET_OK) { + allocator.deallocate(logdir, allocator.state); // We couldn't get the system time, so get out of here without setting up // logging. return RCL_LOGGING_RET_ERROR; @@ -124,6 +125,7 @@ rcl_logging_ret_t rcl_logging_external_initialize( // Get the program name. char * executable_name = rcutils_get_executable_name(allocator); if (executable_name == nullptr) { + allocator.deallocate(logdir, allocator.state); // We couldn't get the program name, so get out of here without setting up // logging. return RCL_LOGGING_RET_ERROR; @@ -132,8 +134,9 @@ rcl_logging_ret_t rcl_logging_external_initialize( char log_name_buffer[512] = {0}; int print_ret = rcutils_snprintf( log_name_buffer, sizeof(log_name_buffer), - "%s/.ros/log/%s_%i_%" PRId64 ".log", homedir, executable_name, + "%s/%s_%i_%" PRId64 ".log", logdir, executable_name, rcutils_get_pid(), ms_since_epoch); + allocator.deallocate(logdir, allocator.state); allocator.deallocate(executable_name, allocator.state); if (print_ret < 0) { RCUTILS_SET_ERROR_MSG("Failed to create log file name string"); diff --git a/rcl_logging_spdlog/CMakeLists.txt b/rcl_logging_spdlog/CMakeLists.txt index 0ae2837..46cfc8b 100644 --- a/rcl_logging_spdlog/CMakeLists.txt +++ b/rcl_logging_spdlog/CMakeLists.txt @@ -13,6 +13,7 @@ endif() find_package(ament_cmake_ros REQUIRED) find_package(rcl_logging_interface REQUIRED) +find_package(rcpputils REQUIRED) find_package(rcutils REQUIRED) find_package(spdlog_vendor REQUIRED) # Provides spdlog on platforms without it. find_package(spdlog REQUIRED) @@ -26,6 +27,7 @@ target_link_libraries(${PROJECT_NAME} spdlog::spdlog) ament_target_dependencies(${PROJECT_NAME} rcl_logging_interface + rcpputils rcutils spdlog ) @@ -47,7 +49,6 @@ if(BUILD_TESTING) performance_test_fixture::performance_test_fixture INTERFACE_INCLUDE_DIRECTORIES) find_package(ament_cmake_gtest REQUIRED) - find_package(rcpputils REQUIRED) ament_add_gtest(test_logging_interface test/test_logging_interface.cpp) if(TARGET test_logging_interface) target_link_libraries(test_logging_interface ${PROJECT_NAME}) diff --git a/rcl_logging_spdlog/package.xml b/rcl_logging_spdlog/package.xml index 4c672b9..ebc7e63 100644 --- a/rcl_logging_spdlog/package.xml +++ b/rcl_logging_spdlog/package.xml @@ -15,6 +15,7 @@ spdlog rcl_logging_interface + rcpputils rcutils spdlog_vendor @@ -23,7 +24,6 @@ ament_lint_auto ament_lint_common performance_test_fixture - rcpputils rcl_logging_packages diff --git a/rcl_logging_spdlog/src/rcl_logging_spdlog.cpp b/rcl_logging_spdlog/src/rcl_logging_spdlog.cpp index daa4cc3..ba400db 100644 --- a/rcl_logging_spdlog/src/rcl_logging_spdlog.cpp +++ b/rcl_logging_spdlog/src/rcl_logging_spdlog.cpp @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include -#include #include #include #include @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "spdlog/spdlog.h" @@ -75,36 +76,20 @@ rcl_logging_ret_t rcl_logging_external_initialize( // To be compatible with ROS 1, we construct a default filename of // the form ~/.ros/log/__.log - // First get the home directory. - const char * homedir = rcutils_get_home_dir(); - if (homedir == nullptr) { - // We couldn't get the home directory; it is not really going to be - // possible to do logging properly, so get out of here without setting - // up logging. - RCUTILS_SET_ERROR_MSG("Failed to get users home directory"); - return RCL_LOGGING_RET_ERROR; - } - - // SPDLOG doesn't automatically create the log directories, so make them - // by hand here. - char name_buffer[4096] = {0}; - int print_ret = rcutils_snprintf(name_buffer, sizeof(name_buffer), "%s/.ros", homedir); - if (print_ret < 0) { - RCUTILS_SET_ERROR_MSG("Failed to create home directory string"); - return RCL_LOGGING_RET_ERROR; - } - if (!rcutils_mkdir(name_buffer)) { - RCUTILS_SET_ERROR_MSG("Failed to create user .ros directory"); - return RCL_LOGGING_RET_ERROR; + char * logdir = nullptr; + rcl_logging_ret_t dir_ret = rcl_logging_get_logging_directory(allocator, &logdir); + if (RCL_LOGGING_RET_OK != dir_ret) { + // We couldn't get the log directory, so get out of here without setting up + // logging. + RCUTILS_SET_ERROR_MSG("Failed to get logging directory"); + return dir_ret; } - print_ret = rcutils_snprintf(name_buffer, sizeof(name_buffer), "%s/.ros/log", homedir); - if (print_ret < 0) { - RCUTILS_SET_ERROR_MSG("Failed to create log directory string"); - return RCL_LOGGING_RET_ERROR; - } - if (!rcutils_mkdir(name_buffer)) { - RCUTILS_SET_ERROR_MSG("Failed to create user log directory"); + // SPDLOG doesn't automatically create the log directories, so create them + rcpputils::fs::path logdir_path(logdir); + if (!rcpputils::fs::create_directories(logdir_path)) { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("Failed to create log directory: %s", logdir); + allocator.deallocate(logdir, allocator.state); return RCL_LOGGING_RET_ERROR; } @@ -112,6 +97,7 @@ rcl_logging_ret_t rcl_logging_external_initialize( rcutils_time_point_value_t now; rcutils_ret_t ret = rcutils_system_time_now(&now); if (ret != RCUTILS_RET_OK) { + allocator.deallocate(logdir, allocator.state); // We couldn't get the system time, so get out of here without setting up // logging. We don't need to call RCUTILS_SET_ERROR_MSG either since // rcutils_system_time_now() already did it. @@ -122,16 +108,19 @@ rcl_logging_ret_t rcl_logging_external_initialize( // Get the program name. char * basec = rcutils_get_executable_name(allocator); if (basec == nullptr) { + allocator.deallocate(logdir, allocator.state); // We couldn't get the program name, so get out of here without setting up // logging. RCUTILS_SET_ERROR_MSG("Failed to get the executable name"); return RCL_LOGGING_RET_ERROR; } - print_ret = rcutils_snprintf( + char name_buffer[4096] = {0}; + int print_ret = rcutils_snprintf( name_buffer, sizeof(name_buffer), - "%s/.ros/log/%s_%i_%" PRId64 ".log", homedir, + "%s/%s_%i_%" PRId64 ".log", logdir, basec, rcutils_get_pid(), ms_since_epoch); + allocator.deallocate(logdir, allocator.state); allocator.deallocate(basec, allocator.state); if (print_ret < 0) { RCUTILS_SET_ERROR_MSG("Failed to create log file name string");