├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── rcl_logging_interface ├── CHANGELOG.rst ├── CMakeLists.txt ├── Doxyfile ├── include │ └── rcl_logging_interface │ │ ├── rcl_logging_interface.h │ │ └── visibility_control.h ├── package.xml ├── src │ └── logging_dir.c └── test │ └── test_get_logging_directory.cpp ├── rcl_logging_noop ├── CHANGELOG.rst ├── CMakeLists.txt ├── Doxyfile ├── README.md ├── package.xml └── src │ └── rcl_logging_noop.cpp └── rcl_logging_spdlog ├── CHANGELOG.rst ├── CMakeLists.txt ├── Doxyfile ├── QUALITY_DECLARATION.md ├── README.md ├── package.xml ├── src └── rcl_logging_spdlog.cpp └── test ├── benchmark └── benchmark_logging_interface.cpp └── test_logging_interface.cpp /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Any contribution that you make to this repository will 2 | be under the Apache 2 License, as dictated by that 3 | [license](http://www.apache.org/licenses/LICENSE-2.0.html): 4 | 5 | ~~~ 6 | 5. Submission of Contributions. Unless You explicitly state otherwise, 7 | any Contribution intentionally submitted for inclusion in the Work 8 | by You to the Licensor shall be under the terms and conditions of 9 | this License, without any additional terms or conditions. 10 | Notwithstanding the above, nothing herein shall supersede or modify 11 | the terms of any separate license agreement you may have executed 12 | with Licensor regarding such Contributions. 13 | ~~~ 14 | 15 | Contributors must sign-off each commit by adding a `Signed-off-by: ...` 16 | line to commit messages to certify that they have the right to submit 17 | the code they are contributing to the project according to the 18 | [Developer Certificate of Origin (DCO)](https://developercertificate.org/). 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository contains several packages which are all related to the ROS logging functionalities. 2 | 3 | ## Packages 4 | 5 | - rcl_logging_interface 6 | - rcl_logging_noop 7 | - rcl_logging_spdlog 8 | -------------------------------------------------------------------------------- /rcl_logging_interface/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package rcl_logging_interface 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 3.3.0 (2025-04-25) 6 | ------------------ 7 | 8 | 3.2.2 (2024-10-03) 9 | ------------------ 10 | 11 | 3.2.1 (2024-07-29) 12 | ------------------ 13 | 14 | 3.2.0 (2024-04-26) 15 | ------------------ 16 | 17 | 3.1.0 (2024-03-28) 18 | ------------------ 19 | * Check allocator validity in some rcl_logging functions (`#116 `_) 20 | If the allocator is zero-initialized, it may cause a segfault when it is 21 | used later in the functions. 22 | * Use (void) in declaration of param-less function (`#114 `_) 23 | * Contributors: Christophe Bedard, Scott K Logan 24 | 25 | 3.0.0 (2024-01-24) 26 | ------------------ 27 | * add file_name_prefix parameter to external log configuration. (`#109 `_) 28 | * Contributors: Tomoya Fujita 29 | 30 | 2.7.1 (2023-12-26) 31 | ------------------ 32 | * Migrate to std::filesystem (`#104 `_) 33 | * Contributors: Kenta Yonekura 34 | 35 | 2.7.0 (2023-09-07) 36 | ------------------ 37 | * Remove the last uses of ament_target_dependencies in this repo. (`#102 `_) 38 | * Contributors: Chris Lalancette 39 | 40 | 2.6.0 (2023-04-27) 41 | ------------------ 42 | 43 | 2.5.1 (2023-04-11) 44 | ------------------ 45 | 46 | 2.5.0 (2023-02-13) 47 | ------------------ 48 | * Update rcl_logging to C++17. (`#98 `_) 49 | * Contributors: Chris Lalancette 50 | 51 | 2.4.3 (2022-11-18) 52 | ------------------ 53 | * Updated maintainers - 2022-11-07 (`#96 `_) 54 | * Contributors: Audrow Nash 55 | 56 | 2.4.2 (2022-11-02) 57 | ------------------ 58 | 59 | 2.4.1 (2022-09-13) 60 | ------------------ 61 | 62 | 2.4.0 (2022-04-29) 63 | ------------------ 64 | 65 | 2.3.0 (2022-03-01) 66 | ------------------ 67 | * Install includes to include/${PROJECT_NAME} (`#85 `_) 68 | * Contributors: Shane Loretz 69 | 70 | 2.2.1 (2022-01-14) 71 | ------------------ 72 | * Fix include order for cpplint (`#84 `_) 73 | Relates to https://github.com/ament/ament_lint/pull/324 74 | * Update maintainers to Chris Lalancette (`#83 `_) 75 | * Contributors: Audrow Nash, Jacob Perron 76 | 77 | 2.2.0 (2021-11-18) 78 | ------------------ 79 | * Fix renamed `rcpputils` header (`#81 `_) 80 | * Contributors: Abrar Rahman Protyasha 81 | 82 | 2.1.3 (2021-09-16) 83 | ------------------ 84 | * Add Doxyfile to rcl_logging_interface package (`#80 `_) 85 | * Update includes after rcutils/get_env.h deprecation (`#75 `_) 86 | * Contributors: Christophe Bedard, Michel Hidalgo 87 | 88 | 2.1.2 (2021-04-06) 89 | ------------------ 90 | 91 | 2.1.1 (2021-01-25) 92 | ------------------ 93 | 94 | 2.1.0 (2020-12-08) 95 | ------------------ 96 | * Update QD to QL 1 (`#66 `_) 97 | * Use rcutils_expand_user in rcl_logging_get_logging_directory (`#59 `_) 98 | * Allow configuring logging directory through environment variables (`#53 `_) 99 | * Update the maintainers. (`#55 `_) 100 | * Contributors: Chris Lalancette, Christophe Bedard, Stephen Brawner 101 | 102 | 2.0.1 (2020-07-21) 103 | ------------------ 104 | 105 | 2.0.0 (2020-06-18) 106 | ------------------ 107 | * Add new package with rcl logging interface (`#41 `_) 108 | * Contributors: Chris Lalancette 109 | -------------------------------------------------------------------------------- /rcl_logging_interface/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | project(rcl_logging_interface) 4 | 5 | # Default to C11 6 | if(NOT CMAKE_C_STANDARD) 7 | set(CMAKE_C_STANDARD 11) 8 | endif() 9 | # Default to C++17 10 | if(NOT CMAKE_CXX_STANDARD) 11 | set(CMAKE_CXX_STANDARD 17) 12 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 13 | endif() 14 | 15 | if(NOT WIN32) 16 | add_compile_options(-Wall -Wextra -Wpedantic) 17 | endif() 18 | 19 | find_package(ament_cmake_ros REQUIRED) 20 | find_package(rcutils REQUIRED) 21 | 22 | set(${PROJECT_NAME}_sources 23 | "src/logging_dir.c" 24 | ) 25 | add_library(${PROJECT_NAME} ${${PROJECT_NAME}_sources}) 26 | target_include_directories(${PROJECT_NAME} PUBLIC 27 | "$" 28 | "$") 29 | target_link_libraries(${PROJECT_NAME} PUBLIC rcutils::rcutils) 30 | target_compile_definitions(${PROJECT_NAME} PRIVATE "RCL_LOGGING_INTERFACE_BUILDING_DLL") 31 | 32 | install( 33 | DIRECTORY include/ 34 | DESTINATION include/${PROJECT_NAME} 35 | ) 36 | install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME} 37 | ARCHIVE DESTINATION lib 38 | LIBRARY DESTINATION lib 39 | RUNTIME DESTINATION bin) 40 | 41 | # Export old-style CMake variables 42 | ament_export_include_directories("include/${PROJECT_NAME}") 43 | 44 | # Export modern CMake targets 45 | ament_export_targets(${PROJECT_NAME}) 46 | 47 | ament_export_dependencies(rcutils) 48 | 49 | if(BUILD_TESTING) 50 | find_package(ament_lint_auto REQUIRED) 51 | ament_lint_auto_find_test_dependencies() 52 | 53 | find_package(ament_cmake_gtest REQUIRED) 54 | find_package(rcpputils REQUIRED) 55 | ament_add_gtest(test_get_logging_directory test/test_get_logging_directory.cpp) 56 | if(TARGET test_get_logging_directory) 57 | target_link_libraries(test_get_logging_directory ${PROJECT_NAME} rcpputils::rcpputils rcutils::rcutils) 58 | endif() 59 | endif() 60 | 61 | ament_package() 62 | -------------------------------------------------------------------------------- /rcl_logging_interface/Doxyfile: -------------------------------------------------------------------------------- 1 | # All settings not listed here will use the Doxygen default values. 2 | 3 | PROJECT_NAME = "rcl_logging_interface" 4 | PROJECT_NUMBER = master 5 | PROJECT_BRIEF = "Interface that rcl_logging backends need to implement." 6 | 7 | INPUT = ./include 8 | RECURSIVE = YES 9 | OUTPUT_DIRECTORY = doc_output 10 | 11 | EXTRACT_ALL = YES 12 | SORT_MEMBER_DOCS = NO 13 | 14 | GENERATE_LATEX = NO 15 | 16 | ENABLE_PREPROCESSING = YES 17 | MACRO_EXPANSION = YES 18 | EXPAND_ONLY_PREDEF = YES 19 | PREDEFINED += RCUTILS_WARN_UNUSED= 20 | PREDEFINED += RCL_LOGGING_INTERFACE_PUBLIC= 21 | 22 | # Tag files that do not exist will produce a warning and cross-project linking will not work. 23 | TAGFILES += "../../../doxygen_tag_files/cppreference-doxygen-web.tag.xml=http://en.cppreference.com/w/" 24 | TAGFILES += "../../../../doxygen_tag_files/rcutils.tag=http://docs.ros2.org/latest/api/rcutils/" 25 | # Uncomment to generate tag files for cross-project linking. 26 | #GENERATE_TAGFILE = "../../../doxygen_tag_files/rcl_logging_interface.tag" 27 | -------------------------------------------------------------------------------- /rcl_logging_interface/include/rcl_logging_interface/rcl_logging_interface.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Open Source Robotics Foundation, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef RCL_LOGGING_INTERFACE__RCL_LOGGING_INTERFACE_H_ 16 | #define RCL_LOGGING_INTERFACE__RCL_LOGGING_INTERFACE_H_ 17 | 18 | #include "rcl_logging_interface/visibility_control.h" 19 | #include "rcutils/allocator.h" 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | typedef enum 26 | { 27 | RCL_LOGGING_RET_OK = 0, 28 | RCL_LOGGING_RET_ERROR = 2, 29 | RCL_LOGGING_RET_INVALID_ARGUMENT = 11, 30 | RCL_LOGGING_RET_CONFIG_FILE_DOESNT_EXIST = 21, 31 | RCL_LOGGING_RET_CONFIG_FILE_INVALID = 22, 32 | } rcl_logging_ret_t; 33 | 34 | /// Initialize the external logging library. 35 | /** 36 | * \param[in] file_name_prefix The prefix for log file name that external 37 | * logging library should use to configure itself. 38 | * If provided, it must be a null terminated C string. 39 | * If set to NULL or the empty string, the logging library will use defaults. 40 | * \param[in] config_file The location of a config file that the external 41 | * logging library should use to configure itself. 42 | * If provided, it must be a null terminated C string. 43 | * If set to NULL or the empty string, the logging library will use defaults. 44 | * \param[in] allocator The allocator to use for memory allocation. This is 45 | * an rcutils_allocator_t rather than an rcl_allocator_t to ensure that the 46 | * rcl_logging_* packages don't have a circular dependency back to rcl. 47 | * \return RCL_LOGGING_RET_OK if initialized successfully. 48 | * \return RCL_LOGGING_RET_ERROR if an unspecified error occurs. 49 | * \return RCL_LOGGING_RET_CONFIG_FILE_DOESNT_EXIST if a config_file is provided 50 | * but the file doesn't exist. 51 | * \return RCL_LOGGING_RET_CONFIG_FILE_INVALID if a config_file is provided but 52 | * the logging backend doesn't understand it. 53 | */ 54 | RCL_LOGGING_INTERFACE_PUBLIC 55 | RCUTILS_WARN_UNUSED 56 | rcl_logging_ret_t 57 | rcl_logging_external_initialize( 58 | const char * file_name_prefix, 59 | const char * config_file, 60 | rcutils_allocator_t allocator); 61 | 62 | /// Free the resources allocated for the external logging system. 63 | /** 64 | * This puts the system into a state equivalent to being uninitialized. 65 | * 66 | * \return RCL_LOGGING_RET_OK if successfully shutdown, or 67 | * \return RCL_LOGGING_RET_ERROR if an unspecified error occurs. 68 | */ 69 | RCL_LOGGING_INTERFACE_PUBLIC 70 | RCUTILS_WARN_UNUSED 71 | rcl_logging_ret_t 72 | rcl_logging_external_shutdown(void); 73 | 74 | /// Log a message. 75 | /** 76 | * \param[in] severity The severity level of the message being logged. 77 | * \param[in] name The name of the logger, must either be a null terminated 78 | * C string or NULL. 79 | * If NULL or empty the root logger will be used. 80 | * \param[in] msg The message to be logged. Must be a null terminated C string. 81 | */ 82 | RCL_LOGGING_INTERFACE_PUBLIC 83 | void 84 | rcl_logging_external_log(int severity, const char * name, const char * msg); 85 | 86 | /// Set the severity level for a logger. 87 | /** 88 | * This function sets the severity level for the specified logger. 89 | * If the name provided is an empty string or NULL it will change the level of 90 | * the root logger. 91 | * 92 | * \param[in] name The name of the logger. 93 | * Must be a null terminated C string or NULL. 94 | * \param[in] level The severity level to be used for the specified logger. 95 | * \return RCL_LOGGING_RET_OK if set successfully, or 96 | * \return RCL_LOGGING_RET_ERROR if an unspecified error occurs. 97 | */ 98 | RCL_LOGGING_INTERFACE_PUBLIC 99 | RCUTILS_WARN_UNUSED 100 | rcl_logging_ret_t rcl_logging_external_set_logger_level(const char * name, int level); 101 | 102 | /// Get the logging directory. 103 | /** 104 | * Uses various environment variables to construct a logging directory path. 105 | * 106 | * Use $ROS_LOG_DIR if ROS_LOG_DIR is set and not empty. 107 | * Otherwise, use $ROS_HOME/log, using ~/.ros for ROS_HOME if not set or if empty. 108 | * 109 | * It also expands an initial '~' to the current user's home directory, 110 | * and converts the path separator if necessary. 111 | * 112 | * If successful, the directory C string should be deallocated using the given allocator when it is 113 | * no longer needed. 114 | * 115 | * \param[in] allocator The allocator to use for memory allocation. 116 | * \param[out] directory The C string pointer at which to write the directory path. 117 | * Only meaningful if the call is successful. Must not be nullptr and must point to nullptr. 118 | * \return RCL_LOGGING_RET_OK if successful, or 119 | * \return RCL_LOGGING_RET_INVALID_ARGUMENT if any arguments are invalid, or 120 | * \return RCL_LOGGING_RET_ERROR if an unspecified error occurs. 121 | */ 122 | RCL_LOGGING_INTERFACE_PUBLIC 123 | rcl_logging_ret_t 124 | rcl_logging_get_logging_directory(rcutils_allocator_t allocator, char ** directory); 125 | 126 | #ifdef __cplusplus 127 | } 128 | #endif 129 | 130 | #endif // RCL_LOGGING_INTERFACE__RCL_LOGGING_INTERFACE_H_ 131 | -------------------------------------------------------------------------------- /rcl_logging_interface/include/rcl_logging_interface/visibility_control.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Open Source Robotics Foundation, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef RCL_LOGGING_INTERFACE__VISIBILITY_CONTROL_H_ 16 | #define RCL_LOGGING_INTERFACE__VISIBILITY_CONTROL_H_ 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | // This logic was borrowed (then namespaced) from the examples on the gcc wiki: 23 | // https://gcc.gnu.org/wiki/Visibility 24 | 25 | #if defined _WIN32 || defined __CYGWIN__ 26 | #ifdef __GNUC__ 27 | #define RCL_LOGGING_INTERFACE_EXPORT __attribute__ ((dllexport)) 28 | #define RCL_LOGGING_INTERFACE_IMPORT __attribute__ ((dllimport)) 29 | #else 30 | #define RCL_LOGGING_INTERFACE_EXPORT __declspec(dllexport) 31 | #define RCL_LOGGING_INTERFACE_IMPORT __declspec(dllimport) 32 | #endif 33 | #ifdef RCL_LOGGING_INTERFACE_BUILDING_DLL 34 | #define RCL_LOGGING_INTERFACE_PUBLIC RCL_LOGGING_INTERFACE_EXPORT 35 | #else 36 | #define RCL_LOGGING_INTERFACE_PUBLIC RCL_LOGGING_INTERFACE_IMPORT 37 | #endif 38 | #define RCL_LOGGING_INTERFACE_PUBLIC_TYPE RCL_LOGGING_INTERFACE_PUBLIC 39 | #define RCL_LOGGING_INTERFACE_LOCAL 40 | #else 41 | #define RCL_LOGGING_INTERFACE_EXPORT __attribute__ ((visibility("default"))) 42 | #define RCL_LOGGING_INTERFACE_IMPORT 43 | #if __GNUC__ >= 4 44 | #define RCL_LOGGING_INTERFACE_PUBLIC __attribute__ ((visibility("default"))) 45 | #define RCL_LOGGING_INTERFACE_LOCAL __attribute__ ((visibility("hidden"))) 46 | #else 47 | #define RCL_LOGGING_INTERFACE_PUBLIC 48 | #define RCL_LOGGING_INTERFACE_LOCAL 49 | #endif 50 | #define RCL_LOGGING_INTERFACE_PUBLIC_TYPE 51 | #endif 52 | 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | 57 | #endif // RCL_LOGGING_INTERFACE__VISIBILITY_CONTROL_H_ 58 | -------------------------------------------------------------------------------- /rcl_logging_interface/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | rcl_logging_interface 5 | 3.3.0 6 | Interface that rcl_logging backends needs to implement. 7 | 8 | Chris Lalancette 9 | William Woodall 10 | 11 | Apache License 2.0 12 | 13 | Chris Lalancette 14 | William Woodall 15 | 16 | ament_cmake_ros 17 | 18 | rcutils 19 | 20 | ament_lint_auto 21 | ament_lint_common 22 | rcpputils 23 | 24 | 25 | ament_cmake 26 | 27 | 28 | -------------------------------------------------------------------------------- /rcl_logging_interface/src/logging_dir.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Open Source Robotics Foundation, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "rcl_logging_interface/rcl_logging_interface.h" 23 | 24 | rcl_logging_ret_t 25 | rcl_logging_get_logging_directory(rcutils_allocator_t allocator, char ** directory) 26 | { 27 | if (NULL == directory) { 28 | RCUTILS_SET_ERROR_MSG("directory argument must not be null"); 29 | return RCL_LOGGING_RET_INVALID_ARGUMENT; 30 | } 31 | RCUTILS_CHECK_ALLOCATOR(&allocator, return RCL_LOGGING_RET_INVALID_ARGUMENT); 32 | if (NULL != *directory) { 33 | RCUTILS_SET_ERROR_MSG("directory argument must point to null"); 34 | return RCL_LOGGING_RET_INVALID_ARGUMENT; 35 | } 36 | 37 | const char * log_dir_env; 38 | const char * err = rcutils_get_env("ROS_LOG_DIR", &log_dir_env); 39 | if (NULL != err) { 40 | RCUTILS_SET_ERROR_MSG("rcutils_get_env failed"); 41 | return RCL_LOGGING_RET_ERROR; 42 | } 43 | if ('\0' != *log_dir_env) { 44 | *directory = rcutils_strdup(log_dir_env, allocator); 45 | if (NULL == *directory) { 46 | RCUTILS_SET_ERROR_MSG("rcutils_strdup failed"); 47 | return RCL_LOGGING_RET_ERROR; 48 | } 49 | } else { 50 | const char * ros_home_dir_env; 51 | err = rcutils_get_env("ROS_HOME", &ros_home_dir_env); 52 | if (NULL != err) { 53 | RCUTILS_SET_ERROR_MSG("rcutils_get_env failed"); 54 | return RCL_LOGGING_RET_ERROR; 55 | } 56 | char * ros_home_dir; 57 | if ('\0' == *ros_home_dir_env) { 58 | ros_home_dir = rcutils_join_path("~", ".ros", allocator); 59 | if (NULL == ros_home_dir) { 60 | RCUTILS_SET_ERROR_MSG("rcutils_join_path failed"); 61 | return RCL_LOGGING_RET_ERROR; 62 | } 63 | } else { 64 | ros_home_dir = rcutils_strdup(ros_home_dir_env, allocator); 65 | if (NULL == ros_home_dir) { 66 | RCUTILS_SET_ERROR_MSG("rcutils_strdup failed"); 67 | return RCL_LOGGING_RET_ERROR; 68 | } 69 | } 70 | *directory = rcutils_join_path(ros_home_dir, "log", allocator); 71 | allocator.deallocate(ros_home_dir, allocator.state); 72 | if (NULL == *directory) { 73 | RCUTILS_SET_ERROR_MSG("rcutils_join_path failed"); 74 | return RCL_LOGGING_RET_ERROR; 75 | } 76 | } 77 | 78 | char * directory_maybe_not_expanded = *directory; 79 | *directory = rcutils_expand_user(directory_maybe_not_expanded, allocator); 80 | allocator.deallocate(directory_maybe_not_expanded, allocator.state); 81 | if (NULL == *directory) { 82 | RCUTILS_SET_ERROR_MSG("rcutils_expand_user failed"); 83 | return RCL_LOGGING_RET_ERROR; 84 | } 85 | 86 | char * directory_maybe_not_native = *directory; 87 | *directory = rcutils_to_native_path(directory_maybe_not_native, allocator); 88 | allocator.deallocate(directory_maybe_not_native, allocator.state); 89 | if (NULL == *directory) { 90 | RCUTILS_SET_ERROR_MSG("rcutils_to_native_path failed"); 91 | return RCL_LOGGING_RET_ERROR; 92 | } 93 | return RCL_LOGGING_RET_OK; 94 | } 95 | -------------------------------------------------------------------------------- /rcl_logging_interface/test/test_get_logging_directory.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Open Source Robotics Foundation, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include "gtest/gtest.h" 20 | 21 | #include "rcpputils/env.hpp" 22 | #include "rcutils/allocator.h" 23 | #include "rcutils/env.h" 24 | #include "rcutils/error_handling.h" 25 | 26 | #include "rcl_logging_interface/rcl_logging_interface.h" 27 | 28 | // This is a helper class that resets an environment 29 | // variable when leaving scope 30 | class RestoreEnvVar 31 | { 32 | public: 33 | explicit RestoreEnvVar(const std::string & name) 34 | : name_(name), 35 | value_(rcpputils::get_env_var(name.c_str())) 36 | { 37 | } 38 | 39 | ~RestoreEnvVar() 40 | { 41 | if (!rcutils_set_env(name_.c_str(), value_.c_str())) { 42 | std::cerr << "Failed to restore value of environment variable: " << name_ << std::endl; 43 | } 44 | } 45 | 46 | private: 47 | const std::string name_; 48 | const std::string value_; 49 | }; 50 | 51 | TEST(test_logging_directory, directory) 52 | { 53 | RestoreEnvVar home_var("HOME"); 54 | RestoreEnvVar userprofile_var("USERPROFILE"); 55 | ASSERT_EQ(true, rcutils_set_env("HOME", nullptr)); 56 | ASSERT_EQ(true, rcutils_set_env("USERPROFILE", nullptr)); 57 | ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", nullptr)); 58 | ASSERT_EQ(true, rcutils_set_env("ROS_HOME", nullptr)); 59 | 60 | rcutils_allocator_t allocator = rcutils_get_default_allocator(); 61 | 62 | // Invalid argument if given a nullptr 63 | EXPECT_EQ( 64 | RCL_LOGGING_RET_INVALID_ARGUMENT, rcl_logging_get_logging_directory(allocator, nullptr)); 65 | EXPECT_TRUE(rcutils_error_is_set()); 66 | rcutils_reset_error(); 67 | // Invalid argument if the C string is not nullptr 68 | char * could_leak = const_cast("/could/be/leaked"); 69 | EXPECT_EQ( 70 | RCL_LOGGING_RET_INVALID_ARGUMENT, rcl_logging_get_logging_directory(allocator, &could_leak)); 71 | EXPECT_TRUE(rcutils_error_is_set()); 72 | rcutils_reset_error(); 73 | 74 | // Fails without any relevant env vars at all (HOME included) 75 | char * directory = nullptr; 76 | EXPECT_EQ(RCL_LOGGING_RET_ERROR, rcl_logging_get_logging_directory(allocator, &directory)); 77 | EXPECT_TRUE(rcutils_error_is_set()); 78 | rcutils_reset_error(); 79 | directory = nullptr; 80 | 81 | // Default case without ROS_LOG_DIR or ROS_HOME being set (but with HOME) 82 | std::filesystem::path fake_home("/fake_home_dir"); 83 | ASSERT_EQ(true, rcutils_set_env("HOME", fake_home.string().c_str())); 84 | ASSERT_EQ(true, rcutils_set_env("USERPROFILE", fake_home.string().c_str())); 85 | std::filesystem::path default_dir = fake_home / ".ros" / "log"; 86 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); 87 | EXPECT_STREQ(directory, default_dir.make_preferred().string().c_str()); 88 | allocator.deallocate(directory, allocator.state); 89 | directory = nullptr; 90 | 91 | // Use $ROS_LOG_DIR if it is set 92 | const char * my_log_dir_raw = "/my/ros_log_dir"; 93 | std::filesystem::path my_log_dir(my_log_dir_raw); 94 | ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", my_log_dir.string().c_str())); 95 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); 96 | EXPECT_STREQ(directory, my_log_dir.make_preferred().string().c_str()); 97 | allocator.deallocate(directory, allocator.state); 98 | directory = nullptr; 99 | // Make sure it converts path separators when necessary 100 | ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", my_log_dir_raw)); 101 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); 102 | EXPECT_STREQ(directory, my_log_dir.make_preferred().string().c_str()); 103 | allocator.deallocate(directory, allocator.state); 104 | directory = nullptr; 105 | // Setting ROS_HOME won't change anything since ROS_LOG_DIR is used first 106 | ASSERT_EQ(true, rcutils_set_env("ROS_HOME", "/this/wont/be/used")); 107 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); 108 | EXPECT_STREQ(directory, my_log_dir.make_preferred().string().c_str()); 109 | allocator.deallocate(directory, allocator.state); 110 | directory = nullptr; 111 | ASSERT_EQ(true, rcutils_set_env("ROS_HOME", nullptr)); 112 | // Empty is considered unset 113 | ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", "")); 114 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); 115 | EXPECT_STREQ(directory, default_dir.make_preferred().string().c_str()); 116 | allocator.deallocate(directory, allocator.state); 117 | directory = nullptr; 118 | // Make sure '~' is expanded to the home directory 119 | ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", "~/logdir")); 120 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); 121 | std::filesystem::path fake_log_dir = fake_home / "logdir"; 122 | EXPECT_STREQ(directory, fake_log_dir.make_preferred().string().c_str()); 123 | allocator.deallocate(directory, allocator.state); 124 | directory = nullptr; 125 | // But it should only be expanded if it's at the beginning 126 | std::filesystem::path prefixed_fake_log_dir("/prefix/~/logdir"); 127 | ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", prefixed_fake_log_dir.string().c_str())); 128 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); 129 | EXPECT_STREQ(directory, prefixed_fake_log_dir.make_preferred().string().c_str()); 130 | allocator.deallocate(directory, allocator.state); 131 | directory = nullptr; 132 | ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", "~")); 133 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); 134 | EXPECT_STREQ(directory, fake_home.make_preferred().string().c_str()); 135 | allocator.deallocate(directory, allocator.state); 136 | directory = nullptr; 137 | std::filesystem::path home_trailing_slash(fake_home.string() + "/"); 138 | ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", "~/")); 139 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); 140 | EXPECT_STREQ(directory, home_trailing_slash.make_preferred().string().c_str()); 141 | allocator.deallocate(directory, allocator.state); 142 | directory = nullptr; 143 | 144 | ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", nullptr)); 145 | 146 | // Without ROS_LOG_DIR, use $ROS_HOME/log 147 | std::filesystem::path fake_ros_home = fake_home / ".fakeroshome"; 148 | ASSERT_EQ(true, rcutils_set_env("ROS_HOME", fake_ros_home.string().c_str())); 149 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); 150 | std::filesystem::path fake_ros_home_log_dir = fake_ros_home / "log"; 151 | EXPECT_STREQ(directory, fake_ros_home_log_dir.make_preferred().string().c_str()); 152 | allocator.deallocate(directory, allocator.state); 153 | directory = nullptr; 154 | // Make sure it converts path separators when necessary 155 | const char * my_ros_home_raw = "/my/ros/home"; 156 | ASSERT_EQ(true, rcutils_set_env("ROS_HOME", my_ros_home_raw)); 157 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); 158 | std::filesystem::path my_ros_home_log_dir = std::filesystem::path(my_ros_home_raw) / "log"; 159 | EXPECT_STREQ(directory, my_ros_home_log_dir.make_preferred().string().c_str()); 160 | allocator.deallocate(directory, allocator.state); 161 | directory = nullptr; 162 | // Empty is considered unset 163 | ASSERT_EQ(true, rcutils_set_env("ROS_HOME", "")); 164 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); 165 | EXPECT_STREQ(directory, default_dir.make_preferred().string().c_str()); 166 | allocator.deallocate(directory, allocator.state); 167 | directory = nullptr; 168 | // Make sure '~' is expanded to the home directory 169 | ASSERT_EQ(true, rcutils_set_env("ROS_HOME", "~/.fakeroshome")); 170 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); 171 | EXPECT_STREQ(directory, fake_ros_home_log_dir.make_preferred().string().c_str()); 172 | allocator.deallocate(directory, allocator.state); 173 | directory = nullptr; 174 | // But it should only be expanded if it's at the beginning 175 | std::filesystem::path prefixed_fake_ros_home("/prefix/~/.fakeroshome"); 176 | std::filesystem::path prefixed_fake_ros_home_log_dir = prefixed_fake_ros_home / "log"; 177 | ASSERT_EQ(true, rcutils_set_env("ROS_HOME", prefixed_fake_ros_home.string().c_str())); 178 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_get_logging_directory(allocator, &directory)); 179 | EXPECT_STREQ(directory, prefixed_fake_ros_home_log_dir.make_preferred().string().c_str()); 180 | allocator.deallocate(directory, allocator.state); 181 | directory = nullptr; 182 | 183 | ASSERT_EQ(true, rcutils_set_env("ROS_HOME", nullptr)); 184 | } 185 | -------------------------------------------------------------------------------- /rcl_logging_noop/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package rcl_logging_noop 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 3.3.0 (2025-04-25) 6 | ------------------ 7 | 8 | 3.2.2 (2024-10-03) 9 | ------------------ 10 | * rcl_logging_interface is only valid path with build environment. (`#122 `_) 11 | * README update and some cleanups. (`#120 `_) 12 | * Contributors: Tomoya Fujita 13 | 14 | 3.2.1 (2024-07-29) 15 | ------------------ 16 | 17 | 3.2.0 (2024-04-26) 18 | ------------------ 19 | 20 | 3.1.0 (2024-03-28) 21 | ------------------ 22 | 23 | 3.0.0 (2024-01-24) 24 | ------------------ 25 | * add file_name_prefix parameter to external log configuration. (`#109 `_) 26 | * Contributors: Tomoya Fujita 27 | 28 | 2.7.1 (2023-12-26) 29 | ------------------ 30 | 31 | 2.7.0 (2023-09-07) 32 | ------------------ 33 | * Remove the last uses of ament_target_dependencies in this repo. (`#102 `_) 34 | * Contributors: Chris Lalancette 35 | 36 | 2.6.0 (2023-04-27) 37 | ------------------ 38 | 39 | 2.5.1 (2023-04-11) 40 | ------------------ 41 | 42 | 2.5.0 (2023-02-13) 43 | ------------------ 44 | * Update rcl_logging to C++17. (`#98 `_) 45 | * Contributors: Chris Lalancette 46 | 47 | 2.4.3 (2022-11-18) 48 | ------------------ 49 | * Updated maintainers - 2022-11-07 (`#96 `_) 50 | * Contributors: Audrow Nash 51 | 52 | 2.4.2 (2022-11-02) 53 | ------------------ 54 | 55 | 2.4.1 (2022-09-13) 56 | ------------------ 57 | 58 | 2.4.0 (2022-04-29) 59 | ------------------ 60 | 61 | 2.3.0 (2022-03-01) 62 | ------------------ 63 | 64 | 2.2.1 (2022-01-14) 65 | ------------------ 66 | * Update maintainers to Chris Lalancette (`#83 `_) 67 | * Contributors: Audrow Nash 68 | 69 | 2.2.0 (2021-11-18) 70 | ------------------ 71 | 72 | 2.1.3 (2021-09-16) 73 | ------------------ 74 | 75 | 2.1.2 (2021-04-06) 76 | ------------------ 77 | 78 | 2.1.1 (2021-01-25) 79 | ------------------ 80 | 81 | 2.1.0 (2020-12-08) 82 | ------------------ 83 | * Make internal dependencies private (`#60 `_) 84 | * Update the maintainers. (`#55 `_) 85 | * Contributors: Chris Lalancette, Shane Loretz 86 | 87 | 2.0.1 (2020-07-21) 88 | ------------------ 89 | * Remove unused pytest dependency. (`#43 `_) 90 | * Contributors: Chris Lalancette 91 | 92 | 2.0.0 (2020-06-18) 93 | ------------------ 94 | * Use new package with rcl logging interface (`#41 `_) 95 | * Contributors: Chris Lalancette 96 | 97 | 1.0.0 (2020-05-26) 98 | ------------------ 99 | 100 | 0.4.0 (2020-04-24) 101 | ------------------ 102 | * Fix CMake warnings about using uninitialized variables (`#30 `_) 103 | * Contributors: Dirk Thomas 104 | 105 | 0.3.3 (2019-10-23) 106 | ------------------ 107 | 108 | 0.3.2 (2019-10-07) 109 | ------------------ 110 | * Enable linters for noop and log4cxx. (`#12 `_) 111 | * Contributors: Steven! Ragnarök 112 | 113 | 0.3.1 (2019-10-03) 114 | ------------------ 115 | 116 | 0.3.0 (2019-09-26) 117 | ------------------ 118 | * remove unused 'dependencies' CMake variable (`#16 `_) 119 | * fix package.xml schema violations (`#15 `_) 120 | * Contributors: Mikael Arguedas 121 | 122 | 0.2.1 (2019-05-08) 123 | ------------------ 124 | * Changing the default location for log files to be a local directory instead of /var/log/ros on linux due to permission issues. (`#9 `_) 125 | * Prototype to put things in ~/.ros/log 126 | * Change the API to add an allocator to logging initialize. (`#10 `_) 127 | * Contributors: Chris Lalancette, Steven! Ragnarök 128 | 129 | 0.2.0 (2019-03-09) 130 | ------------------ 131 | 132 | 0.1.0 (2018-12-07) 133 | ------------------ 134 | * First release. 135 | * Contributors: Nick Burek, William Woodall 136 | -------------------------------------------------------------------------------- /rcl_logging_noop/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | project(rcl_logging_noop) 4 | 5 | # Default to C11 6 | if(NOT CMAKE_C_STANDARD) 7 | set(CMAKE_C_STANDARD 11) 8 | endif() 9 | # Default to C++17 10 | if(NOT CMAKE_CXX_STANDARD) 11 | set(CMAKE_CXX_STANDARD 17) 12 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 13 | endif() 14 | 15 | find_package(ament_cmake_ros REQUIRED) 16 | find_package(rcl_logging_interface REQUIRED) 17 | find_package(rcutils REQUIRED) 18 | 19 | if(NOT WIN32) 20 | add_compile_options(-Wall -Wextra -Wpedantic) 21 | endif() 22 | 23 | add_library(${PROJECT_NAME} src/rcl_logging_noop.cpp) 24 | 25 | target_link_libraries(${PROJECT_NAME} PRIVATE 26 | rcutils::rcutils) 27 | target_link_libraries(${PROJECT_NAME} PUBLIC 28 | rcl_logging_interface::rcl_logging_interface) 29 | 30 | target_compile_definitions(${PROJECT_NAME} PRIVATE "RCL_LOGGING_INTERFACE_BUILDING_DLL") 31 | 32 | if(BUILD_TESTING) 33 | find_package(ament_lint_auto REQUIRED) 34 | ament_lint_auto_find_test_dependencies() 35 | endif() 36 | 37 | install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME} 38 | ARCHIVE DESTINATION lib 39 | LIBRARY DESTINATION lib 40 | RUNTIME DESTINATION bin) 41 | 42 | ament_export_dependencies(rcl_logging_interface) 43 | ament_export_libraries(${PROJECT_NAME}) 44 | ament_export_targets(${PROJECT_NAME}) 45 | ament_package() 46 | -------------------------------------------------------------------------------- /rcl_logging_noop/Doxyfile: -------------------------------------------------------------------------------- 1 | # All settings not listed here will use the Doxygen default values. 2 | 3 | PROJECT_NAME = "rcl_logging_noop" 4 | PROJECT_NUMBER = master 5 | PROJECT_BRIEF = "A library implementation of the rcl_logging interface that does nothing." 6 | 7 | # Use these lines to include the generated logging_macro.h (update install path if needed) 8 | #INPUT = README.md ../../../install_isolated/rcutils/include 9 | #STRIP_FROM_PATH = /Users/william/ros2_ws/install_isolated/rcutils/include 10 | # Otherwise just generate for the local (non-generated header files) 11 | INPUT = README.md ./include 12 | USE_MDFILE_AS_MAINPAGE = README.md 13 | RECURSIVE = YES 14 | OUTPUT_DIRECTORY = doc_output 15 | 16 | EXTRACT_ALL = YES 17 | SORT_MEMBER_DOCS = NO 18 | 19 | GENERATE_LATEX = NO 20 | 21 | ENABLE_PREPROCESSING = YES 22 | MACRO_EXPANSION = YES 23 | EXPAND_ONLY_PREDEF = YES 24 | 25 | # Tag files that do not exist will produce a warning and cross-project linking will not work. 26 | TAGFILES += "../../../doxygen_tag_files/cppreference-doxygen-web.tag.xml=http://en.cppreference.com/w/" 27 | # Uncomment to generate tag files for cross-project linking. 28 | #GENERATE_TAGFILE = "../../../doxygen_tag_files/rcutils.tag" 29 | -------------------------------------------------------------------------------- /rcl_logging_noop/README.md: -------------------------------------------------------------------------------- 1 | # rcl_logging_noop 2 | 3 | Package supporting an implementation of no-op logging functionality. 4 | 5 | [rcl_logging_noop](src/rcl_logging_noop.cpp) logging interface implementation can: 6 | - initialize 7 | - log a message 8 | - set the logger level 9 | - shutdown 10 | 11 | ## Build 12 | 13 | Currently there is no way to select the logging interface implementation without building [rcl](https://github.com/ros2/rcl) with target logging interface implementation. 14 | 15 | ```bash 16 | export RCL_LOGGING_IMPLEMENTATION=rcl_logging_noop 17 | colcon build --symlink-install --cmake-clean-cache --packages-select rcl_logging_noop rcl 18 | ``` 19 | -------------------------------------------------------------------------------- /rcl_logging_noop/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | rcl_logging_noop 5 | 3.3.0 6 | An rcl logger implementation that doesn't do anything with log messages. 7 | 8 | Chris Lalancette 9 | William Woodall 10 | 11 | Apache License 2.0 12 | 13 | Nick Burek 14 | William Woodall 15 | 16 | ament_cmake_ros 17 | python3-empy 18 | 19 | rcl_logging_interface 20 | rcutils 21 | 22 | ament_cmake_gmock 23 | ament_cmake_gtest 24 | ament_lint_common 25 | ament_lint_auto 26 | launch_testing 27 | 28 | rcl_logging_packages 29 | 30 | 31 | ament_cmake 32 | 33 | 34 | -------------------------------------------------------------------------------- /rcl_logging_noop/src/rcl_logging_noop.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Open Source Robotics Foundation, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | rcl_logging_ret_t rcl_logging_external_initialize( 19 | const char * file_name_prefix, 20 | const char * config_file, 21 | rcutils_allocator_t allocator) 22 | { 23 | (void) file_name_prefix; 24 | (void) config_file; 25 | (void) allocator; 26 | return RCL_LOGGING_RET_OK; 27 | } 28 | 29 | rcl_logging_ret_t rcl_logging_external_shutdown() 30 | { 31 | return RCL_LOGGING_RET_OK; 32 | } 33 | 34 | void rcl_logging_external_log(int severity, const char * name, const char * msg) 35 | { 36 | (void) severity; 37 | (void) name; 38 | (void) msg; 39 | } 40 | 41 | rcl_logging_ret_t rcl_logging_external_set_logger_level(const char * name, int level) 42 | { 43 | (void) name; 44 | (void) level; 45 | return RCL_LOGGING_RET_OK; 46 | } 47 | -------------------------------------------------------------------------------- /rcl_logging_spdlog/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package rcl_logging_spdlog 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 3.3.0 (2025-04-25) 6 | ------------------ 7 | * Cleanup overwritten warning messages on error. (`#128 `_) 8 | * Contributors: Chris Lalancette 9 | 10 | 3.2.2 (2024-10-03) 11 | ------------------ 12 | * rcl_logging_interface is only valid path with build environment. (`#122 `_) 13 | * README update and some cleanups. (`#120 `_) 14 | * Contributors: Tomoya Fujita 15 | 16 | 3.2.1 (2024-07-29) 17 | ------------------ 18 | * Updated deprecated API (`#117 `_) 19 | * Contributors: Alejandro Hernández Cordero 20 | 21 | 3.2.0 (2024-04-26) 22 | ------------------ 23 | 24 | 3.1.0 (2024-03-28) 25 | ------------------ 26 | * Check allocator validity in some rcl_logging functions (`#116 `_) 27 | If the allocator is zero-initialized, it may cause a segfault when it is 28 | used later in the functions. 29 | * Cleanup the tests. (`#115 `_) 30 | * Cleanup the tests. 31 | There are a few different fixes in here: 32 | 1. Move away from using "popen" to get the list of files 33 | in a directory. Instead, switch to using the C++ std::filesystem 34 | directory iterator and doing the work ourselves, which is portable 35 | and much less error-prone. 36 | 2. Set the ROS_LOG_DIR for all of the tests in here. This should 37 | make the test resistant to being run in parallel with other tests. 38 | 3. Consistently use rcpputils::set_env_var, rather than a mix 39 | of rcpputils and rcutils. 40 | * Update quality declaration document (`#112 `_) 41 | * Re-order rcl_logging_interface include (`#111 `_) 42 | * Contributors: Chris Lalancette, Christophe Bedard, Scott K Logan 43 | 44 | 3.0.0 (2024-01-24) 45 | ------------------ 46 | * add file_name_prefix parameter to external log configuration. (`#109 `_) 47 | * Contributors: Tomoya Fujita 48 | 49 | 2.7.1 (2023-12-26) 50 | ------------------ 51 | * Migrate to std::filesystem (`#104 `_) 52 | * Contributors: Kenta Yonekura 53 | 54 | 2.7.0 (2023-09-07) 55 | ------------------ 56 | * Remove the last uses of ament_target_dependencies in this repo. (`#102 `_) 57 | * Contributors: Chris Lalancette 58 | 59 | 2.6.0 (2023-04-27) 60 | ------------------ 61 | 62 | 2.5.1 (2023-04-11) 63 | ------------------ 64 | * Mark the benchmark _ as unused. (`#99 `_) 65 | * Contributors: Chris Lalancette 66 | 67 | 2.5.0 (2023-02-13) 68 | ------------------ 69 | * Update rcl_logging to C++17. (`#98 `_) 70 | * Contributors: Chris Lalancette 71 | 72 | 2.4.3 (2022-11-18) 73 | ------------------ 74 | * change flushing behavior for spdlog log files, and add env var to use old style (no explicit flushing) (`#95 `_) 75 | * now flushes every ERROR message and periodically every 5 seconds 76 | * can set ``RCL_LOGGING_SPDLOG_EXPERIMENTAL_OLD_FLUSHING_BEHAVIOR=1`` to get old behavior 77 | * Updated maintainers - 2022-11-07 (`#96 `_) 78 | * Contributors: Audrow Nash, William Woodall 79 | 80 | 2.4.2 (2022-11-02) 81 | ------------------ 82 | * Disable cppcheck for rcl_logging_spdlog. (`#93 `_) 83 | * Contributors: Chris Lalancette 84 | 85 | 2.4.1 (2022-09-13) 86 | ------------------ 87 | * ament_export_dependencies any package with targets we linked against (`#89 `_) 88 | * Contributors: Shane Loretz 89 | 90 | 2.4.0 (2022-04-29) 91 | ------------------ 92 | 93 | 2.3.0 (2022-03-01) 94 | ------------------ 95 | 96 | 2.2.1 (2022-01-14) 97 | ------------------ 98 | * Fix include order for cpplint (`#84 `_) 99 | Relates to https://github.com/ament/ament_lint/pull/324 100 | * Update maintainers to Chris Lalancette (`#83 `_) 101 | * Contributors: Audrow Nash, Jacob Perron 102 | 103 | 2.2.0 (2021-11-18) 104 | ------------------ 105 | * Fix renamed `rcpputils` header (`#81 `_) 106 | * Contributors: Abrar Rahman Protyasha 107 | 108 | 2.1.3 (2021-09-16) 109 | ------------------ 110 | * Update includes after rcutils/get_env.h deprecation (`#75 `_) 111 | * Contributors: Christophe Bedard 112 | 113 | 2.1.2 (2021-04-06) 114 | ------------------ 115 | * updating quality declaration links (re: `ros2/docs.ros2.org#52 `_) (`#73 `_) 116 | * Contributors: shonigmann 117 | 118 | 2.1.1 (2021-01-25) 119 | ------------------ 120 | * Include what you use (`#71 `_) 121 | * Contributors: Ivan Santiago Paunovic 122 | 123 | 2.1.0 (2020-12-08) 124 | ------------------ 125 | * Update QD to QL 1 (`#66 `_) 126 | * Make sure to check return value from external_initialize. (`#65 `_) 127 | * updated QD section 3.i and 3ii and spelling error (`#63 `_) 128 | * rcl_logging_spdlog: Increased QL to 2 in QD 129 | * Updated spdlog QL in QD 130 | * Make internal dependencies private (`#60 `_) 131 | * [rcl_logging_spdlog] Add warnings (`#54 `_) 132 | * Allow configuring logging directory through environment variables (`#53 `_) 133 | * Update the maintainers. (`#55 `_) 134 | * Added benchmark test to rcl_logging_spdlog (`#52 `_) 135 | * Used current_path() function from rcpputils (`#51 `_) 136 | * Add fault injection unittest to increase coverage (`#49 `_) 137 | * Contributors: Alejandro Hernández Cordero, Audrow Nash, Chris Lalancette, Christophe Bedard, Shane Loretz, Stephen Brawner, ahcorde, brawner 138 | 139 | 2.0.1 (2020-07-21) 140 | ------------------ 141 | * Bump QD to level 3 and updated QD (`#44 `_) 142 | * Added Doxyfile and fixed related warnings (`#42 `_) 143 | * Contributors: Alejandro Hernández Cordero 144 | 145 | 2.0.0 (2020-06-18) 146 | ------------------ 147 | * Use new package with rcl logging interface (`#41 `_) 148 | * Increased test coverage (`#40 `_) 149 | * Add Security Vulnerability Policy pointing to REP-2006. 150 | * Rename Quality_Declaration.md -> QUALITY_DECLARATION.md 151 | * Contributors: Chris Lalancette, Scott K Logan 152 | 153 | 1.0.0 (2020-05-26) 154 | ------------------ 155 | * Add some preliminary functional tests (`#36 `_) 156 | * warn about unused return value for set_logger_level (`#38 `_) 157 | * Added features to rcl_logging_spdlog (`#35 `_) 158 | * Added public API documentation for log4cxx and spdlog (`#32 `_) 159 | * Current state Quality Declaration, Contributing and Readme files (`#29 `_) 160 | * Contributors: Alejandro Hernández Cordero, Dirk Thomas, Jorge Perez, Scott K Logan 161 | 162 | 0.4.0 (2020-04-24) 163 | ------------------ 164 | * Export targets in addition to include directories / libraries (`#31 `_) 165 | * Make spdlog an exec_depend (`#27 `_) 166 | * Code style only: wrap after open parenthesis if not in one line (`#24 `_) 167 | * Bypass spdlog singleton registry (`#23 `_) 168 | * Contributors: Chris Lalancette, Dirk Thomas, Ivan Santiago Paunovic 169 | 170 | 0.3.3 (2019-10-23) 171 | ------------------ 172 | * Fix Clang warning about possible uninitialized variable (`#21 `_) 173 | * Contributors: Jacob Perron 174 | 175 | 0.3.2 (2019-10-07) 176 | ------------------ 177 | * spdlog is a header-only library, so the exported dep isn't needed. (`#19 `_) 178 | * Contributors: Chris Lalancette 179 | 180 | 0.3.1 (2019-10-03) 181 | ------------------ 182 | * Implement a backend logger using spdlog. (`#17 `_) 183 | * Contributors: Chris Lalancette 184 | 185 | 0.3.0 (2019-09-26) 186 | ------------------ 187 | 188 | 0.2.1 (2019-05-08) 189 | ------------------ 190 | 191 | 0.2.0 (2019-03-09) 192 | ------------------ 193 | 194 | 0.1.0 (2018-12-07) 195 | ------------------ 196 | -------------------------------------------------------------------------------- /rcl_logging_spdlog/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | project(rcl_logging_spdlog) 4 | 5 | # Default to C11 6 | if(NOT CMAKE_C_STANDARD) 7 | set(CMAKE_C_STANDARD 11) 8 | endif() 9 | # Default to C++17 10 | if(NOT CMAKE_CXX_STANDARD) 11 | set(CMAKE_CXX_STANDARD 17) 12 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 13 | endif() 14 | 15 | find_package(ament_cmake_ros REQUIRED) 16 | find_package(rcl_logging_interface REQUIRED) 17 | find_package(rcpputils REQUIRED) 18 | find_package(rcutils REQUIRED) 19 | find_package(spdlog_vendor REQUIRED) # Provides spdlog on platforms without it. 20 | find_package(spdlog REQUIRED) 21 | 22 | if(NOT WIN32) 23 | add_compile_options(-Wall -Wextra -Wpedantic) 24 | endif() 25 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") 26 | add_compile_options( 27 | -Wformat=2 -Wconversion -Woverloaded-virtual -Wshadow 28 | -Wnon-virtual-dtor -Wold-style-cast -Wcast-qual 29 | ) 30 | endif() 31 | 32 | add_library(${PROJECT_NAME} src/rcl_logging_spdlog.cpp) 33 | target_link_libraries(${PROJECT_NAME} PRIVATE 34 | rcpputils::rcpputils 35 | rcutils::rcutils 36 | spdlog::spdlog) 37 | target_link_libraries(${PROJECT_NAME} PUBLIC 38 | rcl_logging_interface::rcl_logging_interface) 39 | 40 | target_compile_definitions(${PROJECT_NAME} PRIVATE "RCL_LOGGING_INTERFACE_BUILDING_DLL") 41 | 42 | install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME} 43 | ARCHIVE DESTINATION lib 44 | LIBRARY DESTINATION lib 45 | RUNTIME DESTINATION bin) 46 | 47 | if(BUILD_TESTING) 48 | find_package(ament_lint_auto REQUIRED) 49 | # cppcheck 1.90 doesn't understand some of the syntax in spdlog's bundled fmt 50 | # code. Since we already have cppcheck disabled on Linux, just disable it 51 | # completely for this package. 52 | list(APPEND AMENT_LINT_AUTO_EXCLUDE 53 | ament_cmake_cppcheck 54 | ) 55 | ament_lint_auto_find_test_dependencies() 56 | 57 | find_package(performance_test_fixture REQUIRED) 58 | 59 | find_package(ament_cmake_gtest REQUIRED) 60 | ament_add_gmock(test_logging_interface test/test_logging_interface.cpp) 61 | if(TARGET test_logging_interface) 62 | target_link_libraries(test_logging_interface ${PROJECT_NAME} rcpputils::rcpputils) 63 | target_compile_definitions(test_logging_interface PUBLIC RCUTILS_ENABLE_FAULT_INJECTION) 64 | endif() 65 | add_performance_test(benchmark_logging_interface test/benchmark/benchmark_logging_interface.cpp) 66 | if(TARGET benchmark_logging_interface) 67 | target_link_libraries(benchmark_logging_interface ${PROJECT_NAME}) 68 | endif() 69 | endif() 70 | 71 | ament_export_dependencies(rcl_logging_interface) 72 | ament_export_libraries(${PROJECT_NAME}) 73 | ament_export_targets(${PROJECT_NAME}) 74 | ament_package() 75 | -------------------------------------------------------------------------------- /rcl_logging_spdlog/Doxyfile: -------------------------------------------------------------------------------- 1 | # All settings not listed here will use the Doxygen default values. 2 | 3 | PROJECT_NAME = "rcl_logging_spdlog" 4 | PROJECT_NUMBER = master 5 | PROJECT_BRIEF = "Implementation of rcl_logging API for an spdlog backend." 6 | 7 | # Use these lines to include the generated logging_macro.h (update install path if needed) 8 | INPUT = README.md ../../../../install/rcl_logging_interface/include ./QUALITY_DECLARATION.md 9 | 10 | # Otherwise just generate for the local (non-generated header files) 11 | USE_MDFILE_AS_MAINPAGE = README.md 12 | RECURSIVE = YES 13 | OUTPUT_DIRECTORY = doc_output 14 | 15 | EXTRACT_ALL = YES 16 | SORT_MEMBER_DOCS = NO 17 | 18 | GENERATE_LATEX = NO 19 | 20 | ENABLE_PREPROCESSING = YES 21 | MACRO_EXPANSION = YES 22 | EXPAND_ONLY_PREDEF = YES 23 | 24 | # Tag files that do not exist will produce a warning and cross-project linking will not work. 25 | TAGFILES += "../../../doxygen_tag_files/cppreference-doxygen-web.tag.xml=http://en.cppreference.com/w/" 26 | TAGFILES += "../../../doxygen_tag_files/rcutils.tag=http://docs.ros2.org/latest/api/rcutils/" 27 | # Uncomment to generate tag files for cross-project linking. 28 | GENERATE_TAGFILE = "../../../doxygen_tag_files/rcl_logging_spdlog.tag" 29 | -------------------------------------------------------------------------------- /rcl_logging_spdlog/QUALITY_DECLARATION.md: -------------------------------------------------------------------------------- 1 | This document is a declaration of software quality for the `rcl_logging_spdlog` package, based on the guidelines in [REP-2004](https://github.com/ros-infrastructure/rep/blob/rep-2004/rep-2004.rst). 2 | 3 | # rcl_logging_spdlog Quality Declaration 4 | 5 | The package `rcl_logging_spdlog` claims to be in the **Quality Level 1** category. 6 | 7 | Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://www.ros.org/reps/rep-2004.html). 8 | 9 | ## Version Policy [1] 10 | 11 | ### Version Scheme [1.i] 12 | 13 | `rcl_logging_spdlog` uses `semver` according to the recommendation for ROS Core packages in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#versioning). 14 | 15 | ### Version Stability [1.ii] 16 | 17 | Currently this package it is not at or above a stable version, i.e. `>= 1.0.0`. 18 | 19 | ### Public API Declaration [1.iii] 20 | 21 | All symbols in the installed headers are considered part of the public API. 22 | 23 | All installed headers are in the [include](./include/rcl_logging_spdlog) directory of the package, headers in any other folders are not installed and considered private. 24 | 25 | ### API Stability Policy [1.iv] 26 | 27 | `rcl_logging_spdlog` will not break public API within a released ROS distribution, i.e. no major releases once the ROS distribution is released. 28 | 29 | ### ABI Stability Policy [1.v] 30 | 31 | `rcl_logging_spdlog` contains C code and therefore must be concerned with ABI stability, and will maintain ABI stability within a ROS distribution. 32 | 33 | ### ABI and ABI Stability Within a Released ROS Distribution [1.vi] 34 | 35 | `rcl_logging_spdlog` will not break API nor ABI within a released ROS distribution, i.e. no major releases once the ROS distribution is released. 36 | 37 | ## Change Control Process [2] 38 | 39 | `rcl_logging_spdlog` follows the recommended guidelines for ROS Core packages in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#change-control-process). 40 | 41 | ### Change Requests [2.i] 42 | 43 | All changes will occur through a pull request, check [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#change-control-process) for additional information. 44 | 45 | ### Contributor Origin [2.ii] 46 | 47 | This package uses DCO as its confirmation of contributor origin policy. 48 | More information can be found in [CONTRIBUTING](../CONTRIBUTING.md) 49 | 50 | ### Peer Review Policy [2.iii] 51 | 52 | Following the recommended guidelines in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#change-control-process) all pull requests must have at least 1 peer review. 53 | 54 | ### Continuous Integration [2.iv] 55 | 56 | All pull requests must pass CI on all [tier 1 platforms](https://www.ros.org/reps/rep-2000.html#support-tiers) 57 | 58 | Currently nightly results can be seen here: 59 | * [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/testReport/rcl_logging_spdlog/) 60 | * [linux_release](https://ci.ros2.org/view/nightly/job/nightly_linux_release/lastBuild/testReport/rcl_logging_spdlog/) 61 | * [mac_osx_release](https://ci.ros2.org/view/nightly/job/nightly_osx_release/lastBuild/testReport/rcl_logging_spdlog/) 62 | * [windows_release](https://ci.ros2.org/view/nightly/job/nightly_win_rel/lastBuild/testReport/rcl_logging_spdlog/) 63 | 64 | ### Documentation Policy [2.v] 65 | 66 | All pull requests must resolve related documentation changes before merging. 67 | 68 | ## Documentation [3] 69 | 70 | ### Feature Documentation [3.i] 71 | 72 | `rcl_logging_spdlog` has a documented feature list and it is hosted [here](http://docs.ros2.org/latest/api/rcl_logging_spdlog/index.html). 73 | 74 | ### Public API Documentation [3.ii] 75 | 76 | `rcl_logging_spdlog` has documentation of its public API and it is hosted [here](http://docs.ros2.org/latest/api/rcl_logging_spdlog/index.html). 77 | 78 | ### License [3.iii] 79 | 80 | The license for `rcl_logging_spdlog` is Apache 2.0, and a summary is in each source file, the type is declared in the [`package.xml`](./package.xml) manifest file, and a full copy of the license is in the [`LICENSE`](../LICENSE) file. 81 | 82 | There is an automated test which runs a linter that ensures each file has a license statement. 83 | [Here](https://ci.ros2.org/view/nightly/job/nightly_linux_release/lastSuccessfulBuild/testReport/rcl_logging_spdlog/) can be found a list with the latest results of the various linters being run on the package. 84 | 85 | ### Copyright Statements [3.iv] 86 | 87 | The copyright holders each provide a statement of copyright in each source code file in `rcl_logging_spdlog`. 88 | 89 | There is an automated test which runs a linter that ensures each file has at least one copyright statement. 90 | Latest linter result report can be seen [here](https://ci.ros2.org/view/nightly/job/nightly_linux_release/lastSuccessfulBuild/testReport/rcl_logging_spdlog/copyright/). 91 | 92 | ## Testing [4] 93 | 94 | ### Feature Testing [4.i] 95 | 96 | Each feature in `rcl_logging_spdlog` has corresponding tests which simulate typical usage, and they are located in the [`test`](https://github.com/ros2/rcl_logging/tree/rolling/rcl_logging_spdlog/test) directory. 97 | New features are required to have tests before being added. 98 | 99 | Currently nightly test results can be seen here: 100 | 101 | * [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastSuccessfulBuild/testReport/rcl_logging_spdlog/) 102 | * [linux_release](https://ci.ros2.org/view/nightly/job/nightly_linux_release/lastBuild/lastSuccessfulBuild/testReport/rcl_logging_spdlog/) 103 | * [mac_osx_release](https://ci.ros2.org/view/nightly/job/nightly_osx_release/lastBuild/lastSuccessfulBuild/testReport/rcl_logging_spdlog/) 104 | * [windows_release](https://ci.ros2.org/view/nightly/job/nightly_win_rel/lastSuccessfulBuild/testReport/rcl_logging_spdlog/) 105 | 106 | ### Public API Testing [4.ii] 107 | 108 | Each part of the public API has tests, and new additions or changes to the public API require tests before being added. 109 | The tests aim to cover both typical usage and corner cases, but are quantified by contributing to code coverage. 110 | 111 | ### Coverage [4.iii] 112 | 113 | `rcl_logging_spdlog` follows the recommendations for ROS Core packages in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#code-coverage), and opts to use line coverage instead of branch coverage. 114 | 115 | This includes: 116 | 117 | - tracking and reporting line coverage statistics 118 | - achieving and maintaining a reasonable branch line coverage (90-100%) 119 | - no lines are manually skipped in coverage calculations 120 | 121 | Changes are required to make a best effort to keep or increase coverage before being accepted, but decreases are allowed if properly justified and accepted by reviewers. 122 | 123 | Current coverage statistics can be viewed [here](https://ci.ros2.org/job/nightly_linux_coverage/lastSuccessfulBuild/cobertura/src_ros2_rcl_logging_rcl_logging_spdlog_src/). 124 | A description of how coverage statistics are calculated is summarized in this page ["ROS 2 Onboarding Guide"](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#note-on-coverage-runs). 125 | 126 | ### Performance [4.iv] 127 | 128 | `rcl_logging_spdlog` follows the recommendations for performance testing of C code in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#performance), and opts to do performance analysis on each release rather than each change. 129 | 130 | Package and system level performance benchmarks that cover features of `rcl_logging_spdlog` can be found at: 131 | * [Benchmarks](http://build.ros2.org/view/Rci/job/Rci__benchmark_ubuntu_focal_amd64/BenchmarkTable/) 132 | * [Performance](http://build.ros2.org/view/Rci/job/Rci__nightly-performance_ubuntu_focal_amd64/lastCompletedBuild/) 133 | 134 | Changes that introduce regressions in performance must be adequately justified in order to be accepted and merged. 135 | 136 | ### Linters and Static Analysis [4.v] 137 | 138 | `rcl_logging_spdlog` uses and passes all the standard linters and static analysis tools for a C package as described in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#linters-and-static-analysis). 139 | Passing implies there are no linter/static errors when testing against CI of supported platforms. 140 | 141 | Currently nightly results can be seen here: 142 | * [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastSuccessfulBuild/testReport/rcl_logging_spdlog/) 143 | * [linux_release](https://ci.ros2.org/view/nightly/job/nightly_linux_release/lastBuild/lastSuccessfulBuild/testReport/rcl_logging_spdlog/) 144 | * [mac_osx_release](https://ci.ros2.org/view/nightly/job/nightly_osx_release/lastBuild/lastSuccessfulBuild/testReport/rcl_logging_spdlog/) 145 | * [windows_release](https://ci.ros2.org/view/nightly/job/nightly_win_rel/lastSuccessfulBuild/testReport/rcl_logging_spdlog/) 146 | 147 | ## Dependencies [5] 148 | 149 | Below are evaluations of each of `rcl_logging_spdlog`'s run-time and build-time dependencies that have been determined to influence the quality. 150 | 151 | `rcl_logging_spdlog` depends on the ROS packages `rcutils` and `spdlog_vendor`. 152 | 153 | #### `rcutils` 154 | 155 | The `rcutils` package provides an API which contains common utilities and data structures useful when programming in C. 156 | 157 | It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rcutils/blob/rolling/QUALITY_DECLARATION.md). 158 | 159 | #### `spdlog_vendor` 160 | 161 | The `spdlog_vendor` package provides a CMake shim over the spdlog library. 162 | 163 | It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/spdlog_vendor/blob/rolling/QUALITY_DECLARATION.md). 164 | 165 | ### Optional Direct Runtime ROS Dependencies [5.ii] 166 | 167 | `rcl_logging_spdlog` has no optional Direct Runtime ROS dependencies that need to be considered for this declaration. 168 | 169 | ### Direct Runtime non-ROS Dependency [5.iii] 170 | 171 | `rcl_logging_spdlog` has a Direct Runtime non-ROS dependency on the `spdlog` library. 172 | It was declared to be Quality Level 1 [here](https://github.com/ros2/spdlog_vendor/blob/rolling/SPDLOG_QUALITY_DECLARATION.md). 173 | 174 | ## Platform Support [6] 175 | 176 | `rcl_logging_spdlog` supports all of the tier 1 platforms as described in [REP-2000](https://www.ros.org/reps/rep-2000.html#support-tiers), and tests each change against all of them. 177 | 178 | ## Security [7] 179 | 180 | ### Vulnerability Disclosure Policy [7.i] 181 | 182 | This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://www.ros.org/reps/rep-2006.html). 183 | 184 | # Current status Summary 185 | 186 | The chart below compares the requirements in the REP-2004 with the current state of the `rcl` package. 187 | 188 | |Number| Requirement| Current state | 189 | |--|--|--| 190 | |1| **Version policy** |---| 191 | |1.i|Version Policy available | ✓ | 192 | |1.ii|Stable version | ✓ | 193 | |1.iii|Declared public API| ✓ | 194 | |1.iv|API stability policy| ✓ | 195 | |1.v|ABI stability policy| ✓ | 196 | |1.vi_|API/ABI stable within ros distribution| ✓ | 197 | |2| **Change control process** |---| 198 | |2.i| All changes occur on change request | ✓ | 199 | |2.ii| Contributor origin (DCO, CLA, etc) | ✓ | 200 | |2.iii| Peer review policy | ✓ | 201 | |2.iv| CI policy for change requests | ✓ | 202 | |2.v| Documentation policy for change requests | ✓ | 203 | |3| **Documentation** | --- | 204 | |3.i| Per feature documentation | ✓ | 205 | |3.ii| Per public API item documentation | ✓ | 206 | |3.iii| Declared License(s) | ✓ | 207 | |3.iv| Copyright in source files| ✓ | 208 | |3.v.a| Quality declaration linked to README | ✓ | 209 | |3.v.b| Centralized declaration available for peer review | ✓ | 210 | |4| Testing | --- | 211 | |4.i| Feature items tests | ✓ | 212 | |4.ii| Public API tests | ✓ | 213 | |4.iii.a| Using coverage | ✓ | 214 | |4.iii.a| Coverage policy | ✓ | 215 | |4.iv.a| Performance tests (if applicable) | ✓ | 216 | |4.iv.b| Performance tests policy| ✓ | 217 | |4.v.a| Code style enforcement (linters)| ✓ | 218 | |4.v.b| Use of static analysis tools | ✓ | 219 | |5| Dependencies | --- | 220 | |5.i| Must not have ROS lower level dependencies | ✓ | 221 | |5.ii| Optional ROS lower level dependencies| ✓ | 222 | |5.iii| Justifies quality use of non-ROS dependencies |✓| 223 | |6| Platform support | --- | 224 | |6.i| Support targets Tier1 ROS platforms| ✓ | 225 | |7| Security | --- | 226 | |7.i| Vulnerability Disclosure Policy | ✓ | 227 | -------------------------------------------------------------------------------- /rcl_logging_spdlog/README.md: -------------------------------------------------------------------------------- 1 | # rcl_logging_spdlog 2 | 3 | Package supporting an implementation of logging functionality using `spdlog`. 4 | 5 | [rcl_logging_spdlog](src/rcl_logging_spdlog.cpp) logging interface implementation can: 6 | - initialize 7 | - log a message 8 | - set the logger level 9 | - shutdown 10 | 11 | ## Quality Declaration 12 | 13 | This package claims to be in the **Quality Level 1** category, see the [Quality Declaration](./QUALITY_DECLARATION.md) for more details. 14 | -------------------------------------------------------------------------------- /rcl_logging_spdlog/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | rcl_logging_spdlog 5 | 3.3.0 6 | Implementation of rcl_logging API for an spdlog backend. 7 | 8 | Chris Lalancette 9 | William Woodall 10 | 11 | Apache License 2.0 12 | 13 | Chris Lalancette 14 | William Woodall 15 | 16 | ament_cmake_ros 17 | 18 | spdlog_vendor 19 | spdlog 20 | 21 | rcl_logging_interface 22 | rcpputils 23 | rcutils 24 | 25 | spdlog_vendor 26 | spdlog 27 | 28 | ament_lint_auto 29 | ament_lint_common 30 | performance_test_fixture 31 | 32 | rcl_logging_packages 33 | 34 | 35 | ament_cmake 36 | 37 | 38 | -------------------------------------------------------------------------------- /rcl_logging_spdlog/src/rcl_logging_spdlog.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Open Source Robotics Foundation, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "rcpputils/env.hpp" 26 | #include "rcpputils/scope_exit.hpp" 27 | 28 | #include "rcutils/allocator.h" 29 | #include "rcutils/logging.h" 30 | #include "rcutils/process.h" 31 | #include "rcutils/snprintf.h" 32 | #include "rcutils/strdup.h" 33 | #include "rcutils/time.h" 34 | 35 | #include "spdlog/spdlog.h" 36 | #include "spdlog/sinks/basic_file_sink.h" 37 | 38 | #include "rcl_logging_interface/rcl_logging_interface.h" 39 | 40 | static std::mutex g_logger_mutex; 41 | static std::shared_ptr g_root_logger = nullptr; 42 | 43 | static spdlog::level::level_enum map_external_log_level_to_library_level(int external_level) 44 | { 45 | spdlog::level::level_enum level = spdlog::level::level_enum::off; 46 | 47 | // map to the next highest level of severity 48 | if (external_level <= RCUTILS_LOG_SEVERITY_DEBUG) { 49 | level = spdlog::level::level_enum::debug; 50 | } else if (external_level <= RCUTILS_LOG_SEVERITY_INFO) { 51 | level = spdlog::level::level_enum::info; 52 | } else if (external_level <= RCUTILS_LOG_SEVERITY_WARN) { 53 | level = spdlog::level::level_enum::warn; 54 | } else if (external_level <= RCUTILS_LOG_SEVERITY_ERROR) { 55 | level = spdlog::level::level_enum::err; 56 | } else if (external_level <= RCUTILS_LOG_SEVERITY_FATAL) { 57 | level = spdlog::level::level_enum::critical; 58 | } 59 | return level; 60 | } 61 | 62 | namespace 63 | { 64 | 65 | RCL_LOGGING_INTERFACE_LOCAL 66 | bool 67 | get_should_use_old_flushing_behavior() 68 | { 69 | const char * env_var_name = "RCL_LOGGING_SPDLOG_EXPERIMENTAL_OLD_FLUSHING_BEHAVIOR"; 70 | 71 | try { 72 | std::string env_var_value = rcpputils::get_env_var(env_var_name); 73 | 74 | if (env_var_value.empty()) { 75 | // not set 76 | return false; 77 | } 78 | if ("0" == env_var_value) { 79 | // explicitly false 80 | return false; 81 | } 82 | if ("1" == env_var_value) { 83 | // explicitly true 84 | return true; 85 | } 86 | 87 | // unknown value 88 | throw std::runtime_error("unrecognized value: " + env_var_value); 89 | } catch (const std::runtime_error & error) { 90 | throw std::runtime_error( 91 | std::string("failed to get env var '") + env_var_name + "': " + error.what() 92 | ); 93 | } 94 | } 95 | 96 | } // namespace 97 | 98 | rcl_logging_ret_t rcl_logging_external_initialize( 99 | const char * file_name_prefix, 100 | const char * config_file, 101 | rcutils_allocator_t allocator) 102 | { 103 | RCUTILS_CHECK_ALLOCATOR(&allocator, return RCL_LOGGING_RET_INVALID_ARGUMENT); 104 | 105 | std::lock_guard lk(g_logger_mutex); 106 | // It is possible for this to get called more than once in a process (some of 107 | // the tests do this implicitly by calling rclcpp::init more than once). 108 | // If the logger is already setup, don't do anything. 109 | if (g_root_logger != nullptr) { 110 | return RCL_LOGGING_RET_OK; 111 | } 112 | 113 | bool config_file_provided = (nullptr != config_file) && (config_file[0] != '\0'); 114 | if (config_file_provided) { 115 | // TODO(clalancette): implement support for an external configuration file. 116 | RCUTILS_SET_ERROR_MSG( 117 | "spdlog logging backend doesn't currently support external configuration"); 118 | return RCL_LOGGING_RET_ERROR; 119 | } else { 120 | // check RCL_LOGGING_SPDLOG_EXPERIMENTAL_OLD_FLUSHING_BEHAVIOR to see if we 121 | // should change log file flushing behavior 122 | bool should_use_old_flushing_behavior = false; 123 | try { 124 | should_use_old_flushing_behavior = ::get_should_use_old_flushing_behavior(); 125 | } catch (const std::runtime_error & error) { 126 | RCUTILS_SET_ERROR_MSG(error.what()); 127 | return RCL_LOGGING_RET_ERROR; 128 | } 129 | 130 | // To be compatible with ROS 1, we construct a default filename of 131 | // the form ~/.ros/log/__.log 132 | 133 | char * logdir = nullptr; 134 | rcl_logging_ret_t dir_ret = rcl_logging_get_logging_directory(allocator, &logdir); 135 | if (RCL_LOGGING_RET_OK != dir_ret) { 136 | // We couldn't get the log directory, so exit without setting up logging. 137 | RCUTILS_SET_ERROR_MSG_AND_APPEND_PREV_ERROR("Failed to get logging directory"); 138 | return dir_ret; 139 | } 140 | RCPPUTILS_SCOPE_EXIT( 141 | { 142 | allocator.deallocate(logdir, allocator.state); 143 | }); 144 | 145 | std::error_code ec; 146 | std::filesystem::path logdir_path(logdir); 147 | 148 | std::filesystem::create_directories(logdir_path, ec); 149 | // create_directories returns true if it created the directory, and false if it did not. 150 | // This behavior is maintained regardless of whether an error occurred. Since we don't 151 | // actually care whether the directory was created, we only check for errors. 152 | if (ec.value() != 0) { 153 | RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( 154 | "Failed to create log directory '%s': %d", logdir, ec.value()); 155 | return RCL_LOGGING_RET_ERROR; 156 | } 157 | 158 | // Now get the milliseconds since the epoch in the local timezone. 159 | rcutils_time_point_value_t now; 160 | rcutils_ret_t ret = rcutils_system_time_now(&now); 161 | if (ret != RCUTILS_RET_OK) { 162 | // We couldn't get the system time, so get out of here without setting up 163 | // logging. We don't need to call RCUTILS_SET_ERROR_MSG either since 164 | // rcutils_system_time_now() already did it. 165 | return RCL_LOGGING_RET_ERROR; 166 | } 167 | int64_t ms_since_epoch = RCUTILS_NS_TO_MS(now); 168 | 169 | bool file_name_provided = (nullptr != file_name_prefix) && (file_name_prefix[0] != '\0'); 170 | char * basec; 171 | if (file_name_provided) { 172 | basec = rcutils_strdup(file_name_prefix, allocator); 173 | } else { // otherwise, get the program name. 174 | basec = rcutils_get_executable_name(allocator); 175 | } 176 | if (basec == nullptr) { 177 | // We couldn't get the program name, so get out of here without setting up 178 | // logging. 179 | RCUTILS_SET_ERROR_MSG("Failed to get the executable name"); 180 | return RCL_LOGGING_RET_ERROR; 181 | } 182 | RCPPUTILS_SCOPE_EXIT( 183 | { 184 | allocator.deallocate(basec, allocator.state); 185 | }); 186 | char name_buffer[4096] = {0}; 187 | int print_ret = rcutils_snprintf( 188 | name_buffer, sizeof(name_buffer), 189 | "%s/%s_%i_%" PRId64 ".log", logdir, 190 | basec, rcutils_get_pid(), ms_since_epoch); 191 | if (print_ret < 0) { 192 | RCUTILS_SET_ERROR_MSG("Failed to create log file name string"); 193 | return RCL_LOGGING_RET_ERROR; 194 | } 195 | 196 | auto sink = std::make_unique(name_buffer, false); 197 | g_root_logger = std::make_shared("root", std::move(sink)); 198 | if (!should_use_old_flushing_behavior) { 199 | // in this case we should do the new thing (until config files are supported) 200 | // which is to configure the logger to flush periodically and on 201 | // error level messages 202 | spdlog::flush_every(std::chrono::seconds(5)); 203 | g_root_logger->flush_on(spdlog::level::err); 204 | } else { 205 | // the old behavior is to not configure the sink at all, so do nothing 206 | } 207 | 208 | spdlog::register_logger(g_root_logger); 209 | 210 | g_root_logger->set_pattern("%v"); 211 | } 212 | 213 | return RCL_LOGGING_RET_OK; 214 | } 215 | 216 | rcl_logging_ret_t rcl_logging_external_shutdown() 217 | { 218 | spdlog::drop("root"); 219 | g_root_logger = nullptr; 220 | return RCL_LOGGING_RET_OK; 221 | } 222 | 223 | void rcl_logging_external_log(int severity, const char * name, const char * msg) 224 | { 225 | (void)name; 226 | g_root_logger->log(map_external_log_level_to_library_level(severity), msg); 227 | } 228 | 229 | rcl_logging_ret_t rcl_logging_external_set_logger_level(const char * name, int level) 230 | { 231 | (void)name; 232 | 233 | g_root_logger->set_level(map_external_log_level_to_library_level(level)); 234 | 235 | return RCL_LOGGING_RET_OK; 236 | } 237 | -------------------------------------------------------------------------------- /rcl_logging_spdlog/test/benchmark/benchmark_logging_interface.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Open Source Robotics Foundation, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | 24 | #include "performance_test_fixture/performance_test_fixture.hpp" 25 | 26 | using performance_test_fixture::PerformanceTest; 27 | 28 | namespace 29 | { 30 | constexpr const uint64_t kSize = 4096; 31 | } 32 | 33 | class LoggingBenchmarkPerformance : public PerformanceTest 34 | { 35 | public: 36 | void SetUp(benchmark::State & st) 37 | { 38 | rcl_logging_ret_t ret = rcl_logging_external_initialize( 39 | nullptr, 40 | nullptr, 41 | rcutils_get_default_allocator()); 42 | if (ret != RCL_LOGGING_RET_OK) { 43 | st.SkipWithError(rcutils_get_error_string().str); 44 | } 45 | 46 | data = std::string(kSize, '0'); 47 | PerformanceTest::SetUp(st); 48 | } 49 | void TearDown(benchmark::State & st) 50 | { 51 | PerformanceTest::TearDown(st); 52 | rcl_logging_ret_t ret = rcl_logging_external_shutdown(); 53 | if (ret != RCL_LOGGING_RET_OK) { 54 | st.SkipWithError(rcutils_get_error_string().str); 55 | } 56 | } 57 | 58 | static void setLogLevel(int logger_level, benchmark::State & st) 59 | { 60 | rcl_logging_ret_t ret = rcl_logging_external_set_logger_level(nullptr, logger_level); 61 | if (ret != RCL_LOGGING_RET_OK) { 62 | st.SkipWithError(rcutils_get_error_string().str); 63 | } 64 | } 65 | 66 | std::string data; 67 | }; 68 | 69 | BENCHMARK_F(LoggingBenchmarkPerformance, log_level_hit)(benchmark::State & st) 70 | { 71 | setLogLevel(RCUTILS_LOG_SEVERITY_INFO, st); 72 | 73 | rcl_logging_external_log(RCUTILS_LOG_SEVERITY_INFO, nullptr, data.c_str()); 74 | reset_heap_counters(); 75 | 76 | for (auto _ : st) { 77 | RCUTILS_UNUSED(_); 78 | rcl_logging_external_log(RCUTILS_LOG_SEVERITY_INFO, nullptr, data.c_str()); 79 | } 80 | } 81 | 82 | BENCHMARK_F(LoggingBenchmarkPerformance, log_level_miss)(benchmark::State & st) 83 | { 84 | setLogLevel(RCUTILS_LOG_SEVERITY_INFO, st); 85 | for (auto _ : st) { 86 | RCUTILS_UNUSED(_); 87 | rcl_logging_external_log(RCUTILS_LOG_SEVERITY_DEBUG, nullptr, data.c_str()); 88 | } 89 | } 90 | 91 | BENCHMARK_F(PerformanceTest, logging_reinitialize)(benchmark::State & st) 92 | { 93 | rcutils_allocator_t allocator = rcutils_get_default_allocator(); 94 | rcl_logging_ret_t ret = rcl_logging_external_initialize(nullptr, nullptr, allocator); 95 | if (ret != RCL_LOGGING_RET_OK) { 96 | st.SkipWithError(rcutils_get_error_string().str); 97 | } 98 | 99 | reset_heap_counters(); 100 | 101 | for (auto _ : st) { 102 | RCUTILS_UNUSED(_); 103 | ret = rcl_logging_external_initialize(nullptr, nullptr, allocator); 104 | if (ret != RCL_LOGGING_RET_OK) { 105 | st.SkipWithError(rcutils_get_error_string().str); 106 | } 107 | } 108 | 109 | ret = rcl_logging_external_shutdown(); 110 | if (ret != RCL_LOGGING_RET_OK) { 111 | st.SkipWithError(rcutils_get_error_string().str); 112 | } 113 | } 114 | 115 | BENCHMARK_F(PerformanceTest, logging_initialize_shutdown)(benchmark::State & st) 116 | { 117 | rcutils_allocator_t allocator = rcutils_get_default_allocator(); 118 | for (auto _ : st) { 119 | RCUTILS_UNUSED(_); 120 | rcl_logging_ret_t ret = rcl_logging_external_initialize(nullptr, nullptr, allocator); 121 | if (ret != RCL_LOGGING_RET_OK) { 122 | st.SkipWithError(rcutils_get_error_string().str); 123 | } 124 | ret = rcl_logging_external_shutdown(); 125 | if (ret != RCL_LOGGING_RET_OK) { 126 | st.SkipWithError(rcutils_get_error_string().str); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /rcl_logging_spdlog/test/test_logging_interface.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Open Source Robotics Foundation, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "gmock/gmock.h" 22 | 23 | #include "rcl_logging_interface/rcl_logging_interface.h" 24 | 25 | #include "rcpputils/env.hpp" 26 | #include "rcpputils/filesystem_helper.hpp" 27 | #include "rcpputils/scope_exit.hpp" 28 | 29 | #include "rcutils/allocator.h" 30 | #include "rcutils/error_handling.h" 31 | #include "rcutils/logging.h" 32 | #include "rcutils/process.h" 33 | #include "rcutils/strdup.h" 34 | #include "rcutils/testing/fault_injection.h" 35 | 36 | static constexpr int logger_levels[] = 37 | { 38 | RCUTILS_LOG_SEVERITY_UNSET, 39 | RCUTILS_LOG_SEVERITY_DEBUG, 40 | RCUTILS_LOG_SEVERITY_INFO, 41 | RCUTILS_LOG_SEVERITY_WARN, 42 | RCUTILS_LOG_SEVERITY_ERROR, 43 | RCUTILS_LOG_SEVERITY_FATAL, 44 | }; 45 | 46 | // This is a helper class that resets an environment 47 | // variable when leaving scope 48 | class RestoreEnvVar final 49 | { 50 | public: 51 | explicit RestoreEnvVar(const std::string & name) 52 | : name_(name), 53 | value_(rcpputils::get_env_var(name.c_str())) 54 | { 55 | } 56 | 57 | ~RestoreEnvVar() 58 | { 59 | if (!rcpputils::set_env_var(name_.c_str(), value_.c_str())) { 60 | std::cerr << "Failed to restore value of environment variable: " << name_ << std::endl; 61 | } 62 | } 63 | 64 | private: 65 | const std::string name_; 66 | const std::string value_; 67 | }; 68 | 69 | class AllocatorTest : public ::testing::Test 70 | { 71 | public: 72 | AllocatorTest() 73 | : allocator(rcutils_get_default_allocator()), 74 | bad_allocator(get_bad_allocator()), 75 | invalid_allocator(rcutils_get_zero_initialized_allocator()) 76 | { 77 | } 78 | 79 | rcutils_allocator_t allocator; 80 | rcutils_allocator_t bad_allocator; 81 | rcutils_allocator_t invalid_allocator; 82 | 83 | private: 84 | static rcutils_allocator_t get_bad_allocator() 85 | { 86 | rcutils_allocator_t bad_allocator = rcutils_get_default_allocator(); 87 | bad_allocator.allocate = AllocatorTest::bad_malloc; 88 | bad_allocator.reallocate = AllocatorTest::bad_realloc; 89 | return bad_allocator; 90 | } 91 | 92 | static void * bad_malloc(size_t, void *) 93 | { 94 | return nullptr; 95 | } 96 | 97 | static void * bad_realloc(void *, size_t, void *) 98 | { 99 | return nullptr; 100 | } 101 | }; 102 | 103 | class LoggingTest : public ::testing::Test 104 | { 105 | public: 106 | void SetUp() 107 | { 108 | allocator = rcutils_get_default_allocator(); 109 | orig_ros_log_dir_value_ = rcpputils::get_env_var("ROS_LOG_DIR"); 110 | std::filesystem::path log_dir = rcpputils::fs::create_temporary_directory( 111 | "rcl_logging_spdlog"); 112 | 113 | local_log_dir_ = log_dir.string(); 114 | 115 | rcpputils::set_env_var("ROS_LOG_DIR", local_log_dir_.c_str()); 116 | } 117 | 118 | void TearDown() 119 | { 120 | rcpputils::set_env_var("ROS_LOG_DIR", orig_ros_log_dir_value_.c_str()); 121 | if (std::filesystem::remove_all(local_log_dir_) == 0) { 122 | std::cerr << "Failed to remove temporary directory\n"; 123 | } 124 | } 125 | 126 | std::filesystem::path find_single_log(const char * prefix) 127 | { 128 | char * rcl_log_dir = nullptr; 129 | rcl_logging_ret_t dir_ret = rcl_logging_get_logging_directory(allocator, &rcl_log_dir); 130 | if (dir_ret != RCL_LOGGING_RET_OK) { 131 | throw std::runtime_error("Failed to get logging directory"); 132 | } 133 | RCPPUTILS_SCOPE_EXIT( 134 | { 135 | allocator.deallocate(rcl_log_dir, allocator.state); 136 | }); 137 | std::filesystem::path log_dir(rcl_log_dir); 138 | std::string expected_prefix = get_expected_log_prefix(prefix); 139 | 140 | std::filesystem::path found; 141 | std::filesystem::file_time_type found_last_write; 142 | for (const std::filesystem::directory_entry & dir_entry : 143 | std::filesystem::directory_iterator{log_dir}) 144 | { 145 | // If the start of the filename matches the expected_prefix, and this is the newest file 146 | // starting with that prefix, hold onto it to return later. 147 | if (dir_entry.path().filename().string().rfind(expected_prefix, 0) == 0) { 148 | if (found.string().empty() || dir_entry.last_write_time() > found_last_write) { 149 | found = dir_entry.path(); 150 | found_last_write = dir_entry.last_write_time(); 151 | // Even though we found the file, we have to keep looking in case there 152 | // is another file with the same prefix but a newer timestamp. 153 | } 154 | } 155 | } 156 | 157 | return found; 158 | } 159 | 160 | rcutils_allocator_t allocator; 161 | 162 | private: 163 | std::string get_expected_log_prefix(const char * name) 164 | { 165 | char * exe_name; 166 | if (name == nullptr || name[0] == '\0') { 167 | exe_name = rcutils_get_executable_name(allocator); 168 | } else { 169 | exe_name = rcutils_strdup(name, allocator); 170 | } 171 | if (nullptr == exe_name) { 172 | throw std::runtime_error("Failed to determine executable name"); 173 | } 174 | std::stringstream prefix; 175 | prefix << exe_name << "_" << rcutils_get_pid() << "_"; 176 | allocator.deallocate(exe_name, allocator.state); 177 | return prefix.str(); 178 | } 179 | 180 | std::string orig_ros_log_dir_value_; 181 | std::string local_log_dir_; 182 | }; 183 | 184 | TEST_F(AllocatorTest, init_invalid) 185 | { 186 | // Config files are not supported by spdlog 187 | EXPECT_EQ( 188 | RCL_LOGGING_RET_ERROR, 189 | rcl_logging_external_initialize(nullptr, "anything", allocator)); 190 | rcutils_reset_error(); 191 | EXPECT_EQ( 192 | RCL_LOGGING_RET_ERROR, 193 | rcl_logging_external_initialize("anything", nullptr, bad_allocator)); 194 | rcutils_reset_error(); 195 | EXPECT_EQ( 196 | RCL_LOGGING_RET_INVALID_ARGUMENT, 197 | rcl_logging_external_initialize(nullptr, nullptr, invalid_allocator)); 198 | rcutils_reset_error(); 199 | } 200 | 201 | TEST_F(AllocatorTest, init_failure) 202 | { 203 | RestoreEnvVar home_var("HOME"); 204 | RestoreEnvVar userprofile_var("USERPROFILE"); 205 | 206 | // No home directory to write log to 207 | ASSERT_TRUE(rcpputils::set_env_var("HOME", nullptr)); 208 | ASSERT_TRUE(rcpputils::set_env_var("USERPROFILE", nullptr)); 209 | EXPECT_EQ(RCL_LOGGING_RET_ERROR, rcl_logging_external_initialize(nullptr, nullptr, allocator)); 210 | rcutils_reset_error(); 211 | 212 | // Force failure to create directories 213 | std::filesystem::path fake_home = std::filesystem::current_path() / "fake_home_dir"; 214 | ASSERT_TRUE(std::filesystem::create_directories(fake_home)); 215 | ASSERT_TRUE(rcpputils::set_env_var("HOME", fake_home.string().c_str())); 216 | 217 | // ...fail to create .ros dir 218 | std::filesystem::path ros_dir = fake_home / ".ros"; 219 | std::fstream(ros_dir.string(), std::ios_base::out).close(); 220 | EXPECT_EQ(RCL_LOGGING_RET_ERROR, rcl_logging_external_initialize(nullptr, nullptr, allocator)); 221 | rcutils_reset_error(); 222 | ASSERT_TRUE(std::filesystem::remove(ros_dir)); 223 | 224 | // ...fail to create .ros/log dir 225 | ASSERT_TRUE(std::filesystem::create_directories(ros_dir)); 226 | std::filesystem::path ros_log_dir = ros_dir / "log"; 227 | std::fstream(ros_log_dir.string(), std::ios_base::out).close(); 228 | EXPECT_EQ(RCL_LOGGING_RET_ERROR, rcl_logging_external_initialize(nullptr, nullptr, allocator)); 229 | rcutils_reset_error(); 230 | ASSERT_TRUE(std::filesystem::remove(ros_log_dir)); 231 | ASSERT_TRUE(std::filesystem::remove(ros_dir)); 232 | 233 | ASSERT_TRUE(std::filesystem::remove(fake_home)); 234 | } 235 | 236 | TEST_F(LoggingTest, log_file_name_prefix) 237 | { 238 | std::string log_file_path; 239 | // executable name in default 240 | { 241 | ASSERT_EQ(RCL_LOGGING_RET_OK, rcl_logging_external_initialize(nullptr, nullptr, allocator)); 242 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_external_shutdown()); 243 | EXPECT_NO_THROW(log_file_path = find_single_log(nullptr).string()); 244 | } 245 | // falls back to executable name if not nullptr, but empty 246 | { 247 | ASSERT_EQ(RCL_LOGGING_RET_OK, rcl_logging_external_initialize("", nullptr, allocator)); 248 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_external_shutdown()); 249 | EXPECT_NO_THROW(log_file_path = find_single_log(nullptr).string()); 250 | } 251 | // specified by user application 252 | { 253 | ASSERT_EQ(RCL_LOGGING_RET_OK, rcl_logging_external_initialize("logger", nullptr, allocator)); 254 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_external_shutdown()); 255 | EXPECT_NO_THROW(log_file_path = find_single_log("logger").string()); 256 | } 257 | } 258 | 259 | TEST_F(LoggingTest, init_old_flushing_behavior) 260 | { 261 | RestoreEnvVar env_var("RCL_LOGGING_SPDLOG_EXPERIMENTAL_OLD_FLUSHING_BEHAVIOR"); 262 | rcpputils::set_env_var("RCL_LOGGING_SPDLOG_EXPERIMENTAL_OLD_FLUSHING_BEHAVIOR", "1"); 263 | 264 | ASSERT_EQ(RCL_LOGGING_RET_OK, rcl_logging_external_initialize(nullptr, nullptr, allocator)); 265 | 266 | std::stringstream expected_log; 267 | for (int level : logger_levels) { 268 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_external_set_logger_level(nullptr, level)); 269 | 270 | for (int severity : logger_levels) { 271 | std::stringstream ss; 272 | ss << "Message of severity " << severity << " at level " << level; 273 | rcl_logging_external_log(severity, nullptr, ss.str().c_str()); 274 | 275 | if (severity >= level) { 276 | expected_log << ss.str() << std::endl; 277 | } else if (severity == 0 && level == 10) { 278 | // This is a special case - not sure what the right behavior is 279 | expected_log << ss.str() << std::endl; 280 | } 281 | } 282 | } 283 | 284 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_external_shutdown()); 285 | 286 | std::string log_file_path = find_single_log(nullptr).string(); 287 | std::ifstream log_file(log_file_path); 288 | std::stringstream actual_log; 289 | actual_log << log_file.rdbuf(); 290 | EXPECT_EQ( 291 | expected_log.str(), 292 | actual_log.str()) << "Unexpected log contents in " << log_file_path; 293 | } 294 | 295 | TEST_F(LoggingTest, init_explicit_new_flush_behavior) 296 | { 297 | RestoreEnvVar env_var("RCL_LOGGING_SPDLOG_EXPERIMENTAL_OLD_FLUSHING_BEHAVIOR"); 298 | rcpputils::set_env_var("RCL_LOGGING_SPDLOG_EXPERIMENTAL_OLD_FLUSHING_BEHAVIOR", "0"); 299 | 300 | ASSERT_EQ(RCL_LOGGING_RET_OK, rcl_logging_external_initialize(nullptr, nullptr, allocator)); 301 | 302 | std::stringstream expected_log; 303 | for (int level : logger_levels) { 304 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_external_set_logger_level(nullptr, level)); 305 | 306 | for (int severity : logger_levels) { 307 | std::stringstream ss; 308 | ss << "Message of severity " << severity << " at level " << level; 309 | rcl_logging_external_log(severity, nullptr, ss.str().c_str()); 310 | 311 | if (severity >= level) { 312 | expected_log << ss.str() << std::endl; 313 | } else if (severity == 0 && level == 10) { 314 | // This is a special case - not sure what the right behavior is 315 | expected_log << ss.str() << std::endl; 316 | } 317 | } 318 | } 319 | 320 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_external_shutdown()); 321 | 322 | std::string log_file_path = find_single_log(nullptr).string(); 323 | std::ifstream log_file(log_file_path); 324 | std::stringstream actual_log; 325 | actual_log << log_file.rdbuf(); 326 | EXPECT_EQ( 327 | expected_log.str(), 328 | actual_log.str()) << "Unexpected log contents in " << log_file_path; 329 | } 330 | 331 | TEST_F(LoggingTest, init_invalid_flush_setting) 332 | { 333 | RestoreEnvVar env_var("RCL_LOGGING_SPDLOG_EXPERIMENTAL_OLD_FLUSHING_BEHAVIOR"); 334 | rcpputils::set_env_var("RCL_LOGGING_SPDLOG_EXPERIMENTAL_OLD_FLUSHING_BEHAVIOR", "invalid"); 335 | 336 | ASSERT_EQ(RCL_LOGGING_RET_ERROR, rcl_logging_external_initialize(nullptr, nullptr, allocator)); 337 | std::string error_state_str = rcutils_get_error_string().str; 338 | using ::testing::HasSubstr; 339 | ASSERT_THAT( 340 | error_state_str, 341 | HasSubstr("unrecognized value:")); 342 | rcutils_reset_error(); 343 | } 344 | 345 | TEST_F(LoggingTest, full_cycle) 346 | { 347 | ASSERT_EQ(RCL_LOGGING_RET_OK, rcl_logging_external_initialize(nullptr, nullptr, allocator)); 348 | 349 | // Make sure we can call initialize more than once 350 | ASSERT_EQ(RCL_LOGGING_RET_OK, rcl_logging_external_initialize(nullptr, nullptr, allocator)); 351 | 352 | std::stringstream expected_log; 353 | for (int level : logger_levels) { 354 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_external_set_logger_level(nullptr, level)); 355 | 356 | for (int severity : logger_levels) { 357 | std::stringstream ss; 358 | ss << "Message of severity " << severity << " at level " << level; 359 | rcl_logging_external_log(severity, nullptr, ss.str().c_str()); 360 | 361 | if (severity >= level) { 362 | expected_log << ss.str() << std::endl; 363 | } else if (severity == 0 && level == 10) { 364 | // This is a special case - not sure what the right behavior is 365 | expected_log << ss.str() << std::endl; 366 | } 367 | } 368 | } 369 | 370 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_external_shutdown()); 371 | 372 | std::string log_file_path = find_single_log(nullptr).string(); 373 | std::ifstream log_file(log_file_path); 374 | std::stringstream actual_log; 375 | actual_log << log_file.rdbuf(); 376 | EXPECT_EQ( 377 | expected_log.str(), 378 | actual_log.str()) << "Unexpected log contents in " << log_file_path; 379 | } 380 | 381 | TEST_F(LoggingTest, init_fini_maybe_fail_test) 382 | { 383 | RCUTILS_FAULT_INJECTION_TEST( 384 | { 385 | if (RCL_LOGGING_RET_OK == rcl_logging_external_initialize(nullptr, nullptr, allocator)) { 386 | EXPECT_EQ(RCL_LOGGING_RET_OK, rcl_logging_external_shutdown()); 387 | } else { 388 | EXPECT_TRUE(rcutils_error_is_set()); 389 | rcutils_reset_error(); 390 | } 391 | }); 392 | } 393 | --------------------------------------------------------------------------------