diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index e71c8cf64..29c9ae6e5 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -12,6 +12,7 @@ concurrency: jobs: pr-builder: needs: + - check-nightly-ci - changed-files - checks - conda-cpp-build @@ -40,6 +41,18 @@ jobs: # since other jobs depend on it. if: ${{ vars.TELEMETRY_ENABLED == 'true' }} uses: rapidsai/shared-actions/telemetry-dispatch-stash-base-env-vars@main + check-nightly-ci: + # Switch to ubuntu-latest once it defaults to a version of Ubuntu that + # provides at least Python 3.11 (see + # https://docs.python.org/3/library/datetime.html#datetime.date.fromisoformat) + runs-on: ubuntu-24.04 + env: + RAPIDS_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Check if nightly CI is passing + uses: rapidsai/shared-actions/check_nightly_success/dispatch@main + with: + repo: rmm changed-files: needs: - telemetry-setup diff --git a/.gitignore b/.gitignore index df9d920d5..52ad86cbc 100644 --- a/.gitignore +++ b/.gitignore @@ -155,9 +155,6 @@ ENV/ # mypy .mypy_cache/ -# RMM log files -rmm_log.txt - # cibuildwheel /wheelhouse diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 647bf76c3..85e30844a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: - id: trailing-whitespace - id: end-of-file-fixer - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.2 + rev: v0.8.6 hooks: - id: ruff args: ["--fix"] @@ -82,7 +82,7 @@ repos: - id: verify-copyright - id: verify-alpha-spec - repo: https://github.com/rapidsai/dependency-file-generator - rev: v1.16.0 + rev: v1.17.0 hooks: - id: rapids-dependency-file-generator args: ["--clean"] diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c5bd5428..859e4022e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # ============================================================================= -# Copyright (c) 2018-2024, NVIDIA CORPORATION. +# Copyright (c) 2018-2025, NVIDIA CORPORATION. # # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except # in compliance with the License. You may obtain a copy of the License at @@ -73,10 +73,9 @@ rapids_find_package( # add third party dependencies using CPM rapids_cpm_init() -add_subdirectory(rapids_logger) +include(${rapids-cmake-dir}/cpm/rapids_logger.cmake) +rapids_cpm_rapids_logger() rapids_make_logger(rmm EXPORT_SET rmm-exports) -add_library(rmm::rmm_logger ALIAS rmm_logger) -add_library(rmm::rmm_logger_impl ALIAS rmm_logger_impl) include(cmake/thirdparty/get_cccl.cmake) include(cmake/thirdparty/get_nvtx.cmake) diff --git a/README.md b/README.md index 1d1f7a8ee..62ac48805 100644 --- a/README.md +++ b/README.md @@ -647,8 +647,8 @@ set to `True`. The log file name can be set using the `log_file_name` parameter. RMM includes a debug logger which can be enabled to log trace and debug information to a file. This information can show when errors occur, when additional memory is allocated from upstream resources, -etc. The default log file is `rmm_log.txt` in the current working directory, but the environment -variable `RMM_DEBUG_LOG_FILE` can be set to specify the path and file name. +etc. By default output is logged to stderr, but the environment variable +`RMM_DEBUG_LOG_FILE` can be set to specify a path and file name to dump the logs to instead. There is a CMake configuration variable `RMM_LOGGING_LEVEL`, which can be set to enable compilation of more detailed logging. The default is `INFO`. Available levels are `TRACE`, `DEBUG`, `INFO`, diff --git a/rapids_logger/CMakeLists.txt b/rapids_logger/CMakeLists.txt deleted file mode 100644 index fd50276ca..000000000 --- a/rapids_logger/CMakeLists.txt +++ /dev/null @@ -1,177 +0,0 @@ -# ============================================================================= -# Copyright (c) 2024, NVIDIA CORPORATION. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except -# in compliance with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed under the License -# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express -# or implied. See the License for the specific language governing permissions and limitations under -# the License. -# ============================================================================= - -# cmake-lint: disable=R0915 - -include_guard(GLOBAL) - -cmake_minimum_required(VERSION 3.26.4 FATAL_ERROR) - -include(../rapids_config.cmake) - -include(rapids-cmake) -include(rapids-cpm) - -project( - RAPIDS_LOGGER - VERSION 0.0.1 - LANGUAGES CXX) - -rapids_cmake_build_type(Release) - -rapids_cpm_init() - -include(../cmake/thirdparty/get_spdlog.cmake) - -#[=======================================================================[.rst: -rapids_make_logger ------------------- - -Generate a logger implementation customized for the specified namespace. - -.. code-block:: cmake - - rapids_make_logger( - [EXPORT_SET ] - [LOGGER_TARGET ] - [LOGGER_HEADER_DIR ] - [LOGGER_MACRO_PREFIX ] - ) - -This function produces an interface target named that, when linked to by other targets, provides them a header file that defines a logger interface for the specified namespace. The logger is generated from the provided template files and is configured to use the specified namespace and macro prefix. The generated logger is placed in the specified header directory. - -The logger implementation lives in a separate header file that is not included in the declaration header. The logger implementation is compiled into a separate target that must be linked to by any target that uses the logger. - - -``logger_namespace`` - The namespace for which to generate the logger implementation. - -``EXPORT_SET`` - The name of the export set to which the logger target should be added. If not specified, the logger target is not added to any export set. - -``LOGGER_TARGET`` - The name of the logger (and logger impl) target to create. If not specified, defaults to _logger. - -``LOGGER_HEADER_DIR`` - The directory in which to place the generated logger header file. If not specified, the logger header file is placed in include/. - -``LOGGER_MACRO_PREFIX`` - The prefix to use for the logger macros. If not specified, the macro prefix is the uppercase version of the logger namespace. - -Result Targets -^^^^^^^^^^^^^^^^ - is an interface target that provides the logger interface for the specified namespace. - - _impl is an interface target that provides the logger implementation for the specified namespace. This target must be linked to by any target that uses the logger. Targets linking to this target will have the logger implementation compiled into them. - -Examples -^^^^^^^^ - -Example on how to use :cmake:command:`rapids_make_logger`. - - -.. code-block:: cmake - - # Generate a logger for the namespace "rapids" and associate it with the - # export set "rapids-exports". - rapids_make_logger(rapids - EXPORT_SET rapids-exports - ) - - # Generate a logger for the namespace "rmm" that does not support logging. - rapids_make_logger(rapids) - - -#]=======================================================================] -function(rapids_make_logger logger_namespace) - list(APPEND CMAKE_MESSAGE_CONTEXT "rapids_make_logger") - - set(_rapids_options) - set(_rapids_one_value EXPORT_SET LOGGER_TARGET LOGGER_HEADER_DIR LOGGER_MACRO_PREFIX) - set(_rapids_multi_value) - cmake_parse_arguments(_RAPIDS "${_rapids_options}" "${_rapids_one_value}" - "${_rapids_multi_value}" ${ARGN}) - - # Most arguments are optional and can be inferred from the namespace by default. - set(_RAPIDS_LOGGER_NAMESPACE ${logger_namespace}) - if(NOT _RAPIDS_LOGGER_TARGET) - set(_RAPIDS_LOGGER_TARGET "${logger_namespace}_logger") - endif() - if(NOT _RAPIDS_LOGGER_HEADER_DIR) - set(_RAPIDS_LOGGER_HEADER_DIR "include/${logger_namespace}") - endif() - if(NOT _RAPIDS_LOGGER_MACRO_PREFIX) - string(TOUPPER ${logger_namespace} _RAPIDS_LOGGER_MACRO_PREFIX) - endif() - - # All paths are computed relative to the current source/binary dir of the file from which the - # function is invoked. As a result we cannot use relative paths here because CMake will root these - # paths incorrectly for configure_file/install. - set(BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/${_RAPIDS_LOGGER_HEADER_DIR}) - # TODO: Verify that installation works correctly with prefix removed. - set(INSTALL_DIR ${_RAPIDS_LOGGER_HEADER_DIR}) - - set(LOGGER_OUTPUT_FILE ${BUILD_DIR}/logger.hpp) - configure_file(${CMAKE_CURRENT_FUNCTION_LIST_DIR}/logger.hpp.in ${LOGGER_OUTPUT_FILE}) - install(FILES ${LOGGER_OUTPUT_FILE} DESTINATION ${INSTALL_DIR}) - - set(LOGGER_IMPL_OUTPUT_FILE ${BUILD_DIR}/logger_impl/logger_impl.hpp) - configure_file(${CMAKE_CURRENT_FUNCTION_LIST_DIR}/logger_impl.hpp.in ${LOGGER_IMPL_OUTPUT_FILE}) - install(FILES ${LOGGER_IMPL_OUTPUT_FILE} DESTINATION ${INSTALL_DIR}/logger_impl) - - add_library(${_RAPIDS_LOGGER_TARGET} INTERFACE) - include(GNUInstallDirs) - # Note: The BUILD_INTERFACE setting assumes that LOGGER_HEADER_DIR is the subdirectory of - # CMAKE_INSTALL_INCLUDEDIR relative to which all includes are rooted in the C++ code files. I - # think that is a safe assumption though since if it were violated then the INSTALL_INTERFACE - # would not only be incorrect (if computed using LOGGER_HEADER_DIR), but it would also break - # consumers of the installed package who expect to be able to write `#include - # <${LOGGER_HEADER_DIR/include\//}/logger.hpp>` and have it work. - target_include_directories( - ${_RAPIDS_LOGGER_TARGET} - INTERFACE "$" - "$") - target_compile_features(${_RAPIDS_LOGGER_TARGET} INTERFACE cxx_std_17) - - # Create an interface target that will trigger compilation of the logger implementation in any - # target that is linked to it. - set(LOGGER_IMPL_SRC_OUTPUT_FILE ${BUILD_DIR}/logger_impl/logger.cpp) - configure_file(${CMAKE_CURRENT_FUNCTION_LIST_DIR}/logger.cpp.in ${LOGGER_IMPL_SRC_OUTPUT_FILE}) - install(FILES ${LOGGER_IMPL_SRC_OUTPUT_FILE} DESTINATION ${INSTALL_DIR}/logger_impl) - - # Note that we cannot specify the source files directly in add_library, see the CMake - # documentation explaining that these do not populate INTERFACE_SOURCES. - # https://cmake.org/cmake/help/latest/command/add_library.html#interface-with-sources - set(impl_target ${_RAPIDS_LOGGER_TARGET}_impl) - add_library(${impl_target} INTERFACE) - target_sources( - ${impl_target} - INTERFACE $ - $) - target_link_libraries(${impl_target} INTERFACE ${_RAPIDS_LOGGER_TARGET} - spdlog::spdlog_header_only) - set_target_properties(${impl_target} PROPERTIES POSITION_INDEPENDENT_CODE ON - INTERFACE_POSITION_INDEPENDENT_CODE ON) - - set(_install_export) - if(_RAPIDS_EXPORT_SET) - set(_install_export EXPORT ${_RAPIDS_EXPORT_SET}) - endif() - - install(TARGETS ${_RAPIDS_LOGGER_TARGET} ${_install_export}) - if(TARGET ${impl_target}) - install(TARGETS ${impl_target} ${_install_export}) - endif() - -endfunction() diff --git a/rapids_logger/README.md b/rapids_logger/README.md deleted file mode 100644 index 00e505fc7..000000000 --- a/rapids_logger/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# About - -The `rapids-logger` project defines an easy way to produce a project-specific logger using the excellent [spdlog](https://github.com/gabime/spdlog) package. -The goal of this project is to ensure that projects wishing to provide their own logger may do so easily without needing to reimplement their own custom wrappers around spdlog. -A core goal of the project is to ensure that the custom logger implementation does not leak any spdlog symbols, allowing the safe coexistence of different projects in the same environment even if they use different versions of spdlog. -That goal is the primary reason to prefer using this project rather than directly exposing a specialized instance of a spdlog logger in your own project. - -`rapids-logger` is designed to be used via CMake. -Its CMake defines a function `rapids_make_logger` that can be used to produce a project-specific logger class in a provided namespace. -The resulting logger exposes spdlog-like functionality via the [PImpl idiom](https://en.cppreference.com/w/cpp/language/pimpl) to avoid exposing spdlog symbols publicly. -It uses CMake and template C++ files to generate a public header file to describe the user interface and an inline header that should be placed in a single TU by consumers to compile the implementation. -To simplify usage, each invocation of the function produces two CMake targets, one representing the public header and one representing a trivial source file including the inline header. -Projects using `rapids-logger` should make the first target part of their public link interface while the latter should be linked to privately so that it is compiled into the project's library without public exposure. - -To mirror spdlog, each generated logger also ships with a set of logging macros `_LOG_` that may be used to control logging at compile-time as well as runtime using a compile-time variable `_LOG_ACTIVE_LEVEL`. -For example, a project called "rapids" will be able to write code like this: -``` -RAPIDS_LOG_WARN("Some message to be shown when the warning level is enabled"); -``` -and control whether that warning is shown by compiling the code with `RAPIDS_LOG_ACTIVE_LEVEL=RAPIDS_LOG_LEVEL_WARN`. -Each project is endowed with its own definition of levels, so different projects in the same environment may be safely configured independently of each other and of spdlog. -Each project is also given a `default_logger` function that produces a global logger that may be used anywhere, but projects may also freely instantiate additional loggers as needed. diff --git a/rapids_logger/logger.cpp.in b/rapids_logger/logger.cpp.in deleted file mode 100644 index 36bb2ce02..000000000 --- a/rapids_logger/logger.cpp.in +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2024, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "logger_impl.hpp" diff --git a/rapids_logger/logger.hpp.in b/rapids_logger/logger.hpp.in deleted file mode 100644 index cd2bb2c79..000000000 --- a/rapids_logger/logger.hpp.in +++ /dev/null @@ -1,504 +0,0 @@ -/* - * Copyright (c) 2024, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include - -namespace __attribute__((visibility("default"))) @_RAPIDS_LOGGER_NAMESPACE@ { - -// These values must be kept in sync with spdlog! -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_TRACE 0 -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_DEBUG 1 -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_INFO 2 -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_WARN 3 -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_ERROR 4 -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_CRITICAL 5 -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_OFF 6 - -/** - * @brief The log levels supported by the logger. - * - * These levels correspond to the levels defined by spdlog. - */ -enum class level_enum : int32_t { - trace = @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_TRACE, - debug = @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_DEBUG, - info = @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_INFO, - warn = @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_WARN, - error = @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_ERROR, - critical = @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_CRITICAL, - off = @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_OFF, - n_levels -}; - -namespace detail { -// Forward declare the implementation classes. -class logger_impl; -class sink_impl; -} - -// Forward declare for the sink for the logger to use. -class sink; -using sink_ptr = std::shared_ptr; - -/** - * @class logger - * @brief A logger class that either uses the real implementation (via spdlog) or performs no-ops if - * not supported. - */ -class logger { - public: - logger() = delete; ///< Not default constructible - logger(logger const&) = delete; ///< Not copy constructible - logger& operator=(logger const&) = delete; ///< Not copy assignable - - logger(logger&& other); ///< @default_move_constructor - logger& operator=(logger&& other); ///< @default_move_assignment{logger} - - /** - * @brief A class to manage a vector of sinks. - * - * This class is used internally by the logger class to manage its sinks. It handles synchronization of the sinks with the sinks in the underlying spdlog logger such that all vector-like operations performed on this class are reflected in the underlying spdlog logger's set of sinks. - */ - class sink_vector { - public: - using Iterator = std::vector::iterator; ///< The iterator type - using ConstIterator = std::vector::const_iterator; ///< The const iterator type - - /** - * @brief Construct a new sink_vector object - * - * @param parent The logger whose sinks are being managed - * @param sinks The sinks to manage - */ - explicit sink_vector(logger& parent, std::vector sinks={}) : parent{parent}, sinks_{sinks} {} - - /** - * @brief Add a sink to the vector. - * - * @param sink The sink to add - */ - void push_back(sink_ptr const& sink); - - /** - * @brief Add a sink to the vector. - * - * @param sink The sink to add - */ - void push_back(sink_ptr&& sink); - - /** - * @brief Remove the last sink from the vector. - */ - void pop_back(); - - /** - * @brief Remove all sinks from the vector. - */ - void clear(); - - /** - * @brief Get an iterator to the beginning of the vector. - * - * @return Iterator The iterator - */ - Iterator begin() { return sinks_.begin(); } - - /** - * @brief Get an iterator to the end of the vector. - * - * @return Iterator The iterator - */ - Iterator end() { return sinks_.end(); } - - /** - * @brief Get a const iterator to the beginning of the vector. - * - * @return ConstIterator The const iterator - */ - ConstIterator begin() const { return sinks_.begin(); } - - /** - * @brief Get a const iterator to the end of the vector. - * - * @return ConstIterator The const iterator - */ - ConstIterator end() const { return sinks_.end(); } - - /** - * @brief Get a const iterator to the beginning of the vector. - * - * @return ConstIterator The const iterator - */ - ConstIterator cbegin() const { return sinks_.cbegin(); } - - /** - * @brief Get a const iterator to the end of the vector. - * - * @return ConstIterator The const iterator - */ - ConstIterator cend() const { return sinks_.cend(); } - private: - logger& parent; ///< The logger this vector belongs to - std::vector sinks_; ///< The sinks - }; - - // TODO: When we migrate to C++20 we can use std::format and format strings - // instead of the printf-style printing used here. - /** - * @brief Format and log a message at the specified level. - * - * This function performs printf-style formatting to avoid the need for fmt - * or spdlog's own templated APIs (which would require exposing spdlog - * symbols publicly) and then invokes the base implementation with the - * preformatted string. - * - * @param lvl The log level - * @param format The format string - * @param args The format arguments - */ - template - void log(level_enum lvl, std::string const& format, Args&&... args) { - auto convert_to_c_string = [](auto&& arg) -> decltype(auto) { - using ArgType = std::decay_t; - if constexpr (std::is_same_v) { - return arg.c_str(); - } else { - return std::forward(arg); - } - }; - - // NOLINTBEGIN(cppcoreguidelines-pro-type-vararg) - auto formatted_size = - std::snprintf(nullptr, 0, format.c_str(), convert_to_c_string(std::forward(args))...); - if (formatted_size < 0) { throw std::runtime_error("Error during formatting."); } - if (formatted_size == 0) { log(lvl, {}); } - auto size = static_cast(formatted_size) + 1; // for null terminator - // NOLINTNEXTLINE(modernize-avoid-c-arrays, cppcoreguidelines-avoid-c-arrays) - std::unique_ptr buf(new char[size]); - std::snprintf(buf.get(), size, format.c_str(), convert_to_c_string(std::forward(args))...); - // NOLINTEND(cppcoreguidelines-pro-type-vararg) - log(lvl, {buf.get(), buf.get() + size - 1}); // drop '\0' - }; - - /** - * @brief Log a message at the TRACE level. - * - * @param format The format string - * @param args The format arguments - */ - template - void trace(std::string const& format, Args&&... args) - { - log(level_enum::trace, format, std::forward(args)...); - } - - /** - * @brief Log a message at the DEBUG level. - * - * @param format The format string - * @param args The format arguments - */ - template - void debug(std::string const& format, Args&&... args) - { - log(level_enum::debug, format, std::forward(args)...); - } - - /** - * @brief Log a message at the INFO level. - * - * @param format The format string - * @param args The format arguments - */ - template - void info(std::string const& format, Args&&... args) - { - log(level_enum::info, format, std::forward(args)...); - } - - /** - * @brief Log a message at the WARN level. - * - * @param format The format string - * @param args The format arguments - */ - template - void warn(std::string const& format, Args&&... args) - { - log(level_enum::warn, format, std::forward(args)...); - } - - /** - * @brief Log a message at the ERROR level. - * - * @param format The format string - * @param args The format arguments - */ - template - void error(std::string const& format, Args&&... args) - { - log(level_enum::error, format, std::forward(args)...); - } - - /** - * @brief Log a message at the CRITICAL level. - * - * @param format The format string - * @param args The format arguments - */ - template - void critical(std::string const& format, Args&&... args) - { - log(level_enum::critical, format, std::forward(args)...); - } - - // Everything below here is conditionally compiled based on whether logging is supported. - /** - * @brief Construct a new logger object - * - * @param name The name of the logger - * @param filename The name of the log file - */ - logger(std::string name, std::string filename); - - /** - * @brief Construct a new logger object - * - * @param name The name of the logger - * @param stream The stream to log to - */ - logger(std::string name, std::ostream& stream); - - /** - * @brief Construct a new logger object - * - * @param name The name of the logger - * @param sinks The sinks to log to - * - * Note that we must use a vector because initializer_lists are not flexible - * enough to support programmatic construction in callers, and an - * iterator-based API would require templating and thus exposing spdlog - * types. - */ - logger(std::string name, std::vector sinks); - - /** - * @brief Destroy the logger object - */ - ~logger(); - - /** - * @brief Log a message at the specified level. - * - * This is the core logging routine that dispatches to spdlog. - * - * @param lvl The log level - * @param message The message to log - */ - void log(level_enum lvl, std::string const& message); - - /** - * @brief Get the sinks for the logger. - * - * @return The sinks - */ - const sink_vector& sinks() const; - - /** - * @brief Get the sinks for the logger. - * - * @return The sinks - */ - sink_vector& sinks(); - - /** - * @brief Get the current log level. - * - * @return The current log level - */ - level_enum level() const; - - /** - * @brief Set the log level. - * - * @param log_level The new log level - */ - void set_level(level_enum log_level); - - /** - * @brief Flush the logger. - */ - void flush(); - - /** - * @brief Flush all writes on the specified level or above. - */ - void flush_on(level_enum log_level); - - /** - * @brief Get the current flush level. - */ - level_enum flush_level() const; - - /** - * @brief Check if the logger should log a message at the specified level. - * - * @param msg_level The level of the message - * @return true if the message should be logged, false otherwise - */ - bool should_log(level_enum msg_level) const; - - /** - * @brief Set the pattern for the logger. - * - * @param pattern The pattern to use - */ - void set_pattern(std::string pattern); - - private: - std::unique_ptr impl; ///< The logger implementation - sink_vector sinks_; ///< The sinks for the logger -}; - -/** - * @brief A sink for the logger. - * - * These sinks are wrappers around the spdlog sinks that allow us to keep the - * spdlog types private and avoid exposing them in the public API. - */ -class sink { - public: - ~sink(); - protected: - explicit sink(std::unique_ptr impl); - std::unique_ptr impl; - // The sink vector needs to be able to pass the underlying sink to the spdlog logger. - friend class logger::sink_vector; -}; - -/** - * @brief A sink that writes to a file. - * - * See spdlog::sinks::basic_file_sink_mt for more information. - */ -class basic_file_sink_mt : public sink { - public: - basic_file_sink_mt(std::string const& filename, bool truncate = false); -}; - -/** - * @brief A sink that writes to an ostream. - * - * See spdlog::sinks::ostream_sink_mt for more information. - */ -class ostream_sink_mt : public sink { - public: - ostream_sink_mt(std::ostream& stream, bool force_flush = false); -}; - - -/** - * @brief Returns the default log filename for the global logger. - * - * If the environment variable `@_RAPIDS_LOGGER_NAMESPACE@_DEBUG_LOG_FILE` is defined, its value is used as the path and - * name of the log file. Otherwise, the file `@_RAPIDS_LOGGER_NAMESPACE@_log.txt` in the current working directory is used. - * - * @return std::string The default log file name. - */ -inline std::string default_log_filename() -{ - auto* filename = std::getenv("@_RAPIDS_LOGGER_MACRO_PREFIX@_DEBUG_LOG_FILE"); - // TODO: Do we prefer rmm's default (a file rmm_log.txt) or cudf's default (a - // stderr sink)? I think the latter is better. - return (filename == nullptr) ? std::string{"@_RAPIDS_LOGGER_NAMESPACE@_log.txt"} : std::string{filename}; -} - -/** - * @brief Get the default logger. - * - * @return logger& The default logger - */ -inline logger& default_logger() -{ - static logger logger_ = [] { - logger logger_ { - "@_RAPIDS_LOGGER_MACRO_PREFIX@", default_log_filename() - }; -#if @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_ACTIVE_LEVEL <= @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_INFO -#ifdef CUDA_API_PER_THREAD_DEFAULT_STREAM - logger_.info("----- @_RAPIDS_LOGGER_MACRO_PREFIX@ LOG BEGIN [PTDS ENABLED] -----"); -#else - logger_.info("----- @_RAPIDS_LOGGER_MACRO_PREFIX@ LOG BEGIN [PTDS DISABLED] -----"); -#endif -#endif - return logger_; - }(); - return logger_; -} - -// Macros for easier logging, similar to spdlog. -#if !defined(@_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_ACTIVE_LEVEL) -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_ACTIVE_LEVEL @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_INFO -#endif - -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOGGER_CALL(logger, level, ...) (logger).log(level, __VA_ARGS__) - -#if @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_ACTIVE_LEVEL <= @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_TRACE -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_TRACE(...) \ - @_RAPIDS_LOGGER_MACRO_PREFIX@_LOGGER_CALL(@_RAPIDS_LOGGER_NAMESPACE@::default_logger(), @_RAPIDS_LOGGER_NAMESPACE@::level_enum::trace, __VA_ARGS__) -#else -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_TRACE(...) (void)0 -#endif - -#if @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_ACTIVE_LEVEL <= @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_DEBUG -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_DEBUG(...) \ - @_RAPIDS_LOGGER_MACRO_PREFIX@_LOGGER_CALL(@_RAPIDS_LOGGER_NAMESPACE@::default_logger(), @_RAPIDS_LOGGER_NAMESPACE@::level_enum::debug, __VA_ARGS__) -#else -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_DEBUG(...) (void)0 -#endif - -#if @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_ACTIVE_LEVEL <= @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_INFO -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_INFO(...) @_RAPIDS_LOGGER_MACRO_PREFIX@_LOGGER_CALL(@_RAPIDS_LOGGER_NAMESPACE@::default_logger(), @_RAPIDS_LOGGER_NAMESPACE@::level_enum::info, __VA_ARGS__) -#else -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_INFO(...) (void)0 -#endif - -#if @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_ACTIVE_LEVEL <= @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_WARN -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_WARN(...) @_RAPIDS_LOGGER_MACRO_PREFIX@_LOGGER_CALL(@_RAPIDS_LOGGER_NAMESPACE@::default_logger(), @_RAPIDS_LOGGER_NAMESPACE@::level_enum::warn, __VA_ARGS__) -#else -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_WARN(...) (void)0 -#endif - -#if @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_ACTIVE_LEVEL <= @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_ERROR -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_ERROR(...) \ - @_RAPIDS_LOGGER_MACRO_PREFIX@_LOGGER_CALL(@_RAPIDS_LOGGER_NAMESPACE@::default_logger(), @_RAPIDS_LOGGER_NAMESPACE@::level_enum::error, __VA_ARGS__) -#else -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_ERROR(...) (void)0 -#endif - -#if @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_ACTIVE_LEVEL <= @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_CRITICAL -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_CRITICAL(...) \ - @_RAPIDS_LOGGER_MACRO_PREFIX@_LOGGER_CALL(@_RAPIDS_LOGGER_NAMESPACE@::default_logger(), @_RAPIDS_LOGGER_NAMESPACE@::level_enum::critical, __VA_ARGS__) -#else -#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_CRITICAL(...) (void)0 -#endif - -} // namespace @_RAPIDS_LOGGER_NAMESPACE@ diff --git a/rapids_logger/logger_impl.hpp.in b/rapids_logger/logger_impl.hpp.in deleted file mode 100644 index d5b467571..000000000 --- a/rapids_logger/logger_impl.hpp.in +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (c) 2024, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "../logger.hpp" - -// Include C headers for which we need symbols to be made public and don't want -// the below symbol hiding pattern to apply. -#include -#include -#include - -// Start hiding before including spdlog headers. -#pragma GCC visibility push(hidden) -// This issue claims to have been resolved in gcc 8, but we still seem to encounter it here. -// The code compiles and links and all tests pass, and nm shows symbols resolved as expected. -// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80947 -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wattributes" - -#include -#include -#include -#pragma GCC diagnostic pop - -#include -#include -#include - -namespace @_RAPIDS_LOGGER_NAMESPACE@ { - -namespace detail { -namespace { - -/** - * @brief Convert a string to a log level. - * - * This function is used to process env-var specifications of log levels. - * @param env_lvl_str The string to convert. - * @return The log level. - */ -level_enum string_to_level(std::string_view const env_lvl_str) -{ - if (env_lvl_str == "TRACE") return level_enum::trace; - if (env_lvl_str == "DEBUG") return level_enum::debug; - if (env_lvl_str == "INFO") return level_enum::info; - if (env_lvl_str == "WARN") return level_enum::warn; - if (env_lvl_str == "ERROR") return level_enum::error; - if (env_lvl_str == "CRITICAL") return level_enum::critical; - if (env_lvl_str == "OFF") return level_enum::off; - std::ostringstream os{}; - os << "Invalid logging level: " << env_lvl_str; - throw std::invalid_argument(os.str()); -} - -/** - * @brief Convert a log level to an spdlog log level. - * - * @param lvl The log level to convert. - * @return The spdlog log level. - */ -spdlog::level::level_enum to_spdlog_level(level_enum lvl) -{ - return static_cast(static_cast(lvl)); -} - -/** - * @brief Convert an spdlog log level to a log level. - * - * @param lvl The spdlog log level to convert. - * @return The log level. - */ -level_enum from_spdlog_level(spdlog::level::level_enum lvl) -{ - return static_cast(static_cast(lvl)); -} -} - -/** - * @brief The sink_impl class is a wrapper around an spdlog sink. - * - * This class is the impl part of the PImpl for the sink. - */ -class sink_impl { -public: - sink_impl(std::shared_ptr sink) : underlying{sink} {} -private: - std::shared_ptr underlying; - // The sink_vector needs to be able to pass the underlying sink to the spdlog logger. - friend class logger::sink_vector; -}; - -/** - * @brief The logger_impl class is a wrapper around an spdlog logger. - * - * This class is the impl part of the PImpl for the logger. - */ -class logger_impl { - public: - logger_impl(std::string name) : underlying{spdlog::logger{name}} { - underlying.set_pattern("[%6t][%H:%M:%S:%f][%-6l] %v"); - auto const env_logging_level = - std::getenv("@_RAPIDS_LOGGER_MACRO_PREFIX@_DEFAULT_LOGGING_LEVEL"); - if (env_logging_level != nullptr) { set_level(detail::string_to_level(env_logging_level)); } - auto const env_flush_level = std::getenv("@_RAPIDS_LOGGER_MACRO_PREFIX@_DEFAULT_FLUSH_LEVEL"); - if (env_flush_level != nullptr) { flush_on(detail::string_to_level(env_flush_level)); } - } - - void log(level_enum lvl, std::string const& message) { underlying.log(to_spdlog_level(lvl), message); } - void set_level(level_enum log_level) { underlying.set_level(to_spdlog_level(log_level)); } - void flush() { underlying.flush(); } - void flush_on(level_enum log_level) { underlying.flush_on(to_spdlog_level(log_level)); } - level_enum flush_level() const { return from_spdlog_level(underlying.flush_level()); } - bool should_log(level_enum lvl) const { return underlying.should_log(to_spdlog_level(lvl)); } - level_enum level() const { return from_spdlog_level(underlying.level()); } - void set_pattern(std::string pattern) { underlying.set_pattern(pattern); } - const std::vector &sinks() const { return underlying.sinks(); } - std::vector &sinks() { return underlying.sinks(); } - -private: - spdlog::logger underlying; ///< The spdlog logger -}; - -} // namespace detail - -// Sink vector functions -void logger::sink_vector::push_back(sink_ptr const& sink) { - sinks_.push_back(sink); - parent.impl->sinks().push_back(sink->impl->underlying); -} -void logger::sink_vector::push_back(sink_ptr&& sink) { - sinks_.push_back(sink); - parent.impl->sinks().push_back(sink->impl->underlying); -} -void logger::sink_vector::pop_back() { - sinks_.pop_back(); - parent.impl->sinks().pop_back(); -} -void logger::sink_vector::clear() { - sinks_.clear(); - parent.impl->sinks().clear(); -} - -// Sink methods -sink::sink(std::unique_ptr impl) : impl{std::move(impl)} {} - -sink::~sink() = default; - -basic_file_sink_mt::basic_file_sink_mt(std::string const& filename, bool truncate) - : sink{std::make_unique(std::make_shared(filename, truncate))} {} - -ostream_sink_mt::ostream_sink_mt(std::ostream& stream, bool force_flush) - : sink{std::make_unique(std::make_shared(stream, force_flush))} {} - -// Logger methods -logger::logger(std::string name, std::string filename) - : impl{std::make_unique(name)}, sinks_{*this} { - sinks_.push_back(std::make_shared(filename, true)); -} - -logger::logger(std::string name, std::ostream& stream) - : impl{std::make_unique(name)}, sinks_{*this} { - sinks_.push_back(std::make_shared(stream)); -} - -logger::logger(std::string name, std::vector sinks) - : impl{std::make_unique(name)}, sinks_{*this} { - for (auto const& s : sinks) { - sinks_.push_back(s); - } -} - -logger::~logger() = default; -logger::logger(logger&& other) = default; -logger& logger::operator=(logger&& other) { - impl = std::move(other.impl); - sinks_.clear(); - for (auto const& s : other.sinks_) { - sinks_.push_back(s); - } - return *this; -} - -void logger::log(level_enum lvl, std::string const& message) { impl->log(lvl, message); } -void logger::set_level(level_enum log_level) { impl->set_level(log_level); } -void logger::flush() { impl->flush(); } -void logger::flush_on(level_enum log_level) { impl->flush_on(log_level); } -level_enum logger::flush_level() const { return impl->flush_level(); } -bool logger::should_log(level_enum lvl) const { return impl->should_log(lvl); } -level_enum logger::level() const { return impl->level(); } -void logger::set_pattern(std::string pattern) { impl->set_pattern(pattern); } -const logger::sink_vector& logger::sinks() const { return sinks_; } -logger::sink_vector& logger::sinks() { return sinks_; } - -} // namespace @_RAPIDS_LOGGER_NAMESPACE@ -// This visibility pragma must be here so that both our logger types and those coming from includes are hidden. -#pragma GCC visibility pop