diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 5c5781245..672073922 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -22,8 +22,8 @@ #include #include #include -#include #include +#include #include diff --git a/cpp/cuopt_cli.cpp b/cpp/cuopt_cli.cpp index a9aeaef77..09910ffcf 100644 --- a/cpp/cuopt_cli.cpp +++ b/cpp/cuopt_cli.cpp @@ -18,8 +18,8 @@ #include #include #include -#include #include +#include #include @@ -75,6 +75,18 @@ static char cuda_module_loading_env[] = "CUDA_MODULE_LOADING=EAGER"; */ inline auto make_async() { return std::make_shared(); } +/** + * @brief Handle logger when error happens before logger is initialized + * @param settings Solver settings + * @return cuopt::init_logger_t + */ +inline cuopt::init_logger_t dummy_logger( + const cuopt::linear_programming::solver_settings_t& settings) +{ + return cuopt::init_logger_t(settings.get_parameter(CUOPT_LOG_FILE), + settings.get_parameter(CUOPT_LOG_TO_CONSOLE)); +} + /** * @brief Run a single file * @param file_path Path to the MPS format input file containing the optimization problem @@ -94,6 +106,7 @@ int run_single_file(const std::string& file_path, settings.set_parameter_from_string(key, val); } } catch (const std::exception& e) { + auto log = dummy_logger(settings); CUOPT_LOG_ERROR("Error: %s", e.what()); return -1; } @@ -113,6 +126,7 @@ int run_single_file(const std::string& file_path, } } if (parsing_failed) { + auto log = dummy_logger(settings); CUOPT_LOG_ERROR("Parsing MPS failed. Exiting!"); return -1; } @@ -122,7 +136,8 @@ int run_single_file(const std::string& file_path, const bool is_mip = (op_problem.get_problem_category() == cuopt::linear_programming::problem_category_t::MIP || - op_problem.get_problem_category() == cuopt::linear_programming::problem_category_t::IP); + op_problem.get_problem_category() == cuopt::linear_programming::problem_category_t::IP) && + !solve_relaxation; try { auto initial_solution = @@ -131,23 +146,36 @@ int run_single_file(const std::string& file_path, : cuopt::linear_programming::solution_reader_t::get_variable_values_from_sol_file( initial_solution_file, mps_data_model.get_variable_names()); - if (is_mip && !solve_relaxation) { + if (is_mip) { auto& mip_settings = settings.get_mip_settings(); if (initial_solution.size() > 0) { mip_settings.add_initial_solution(initial_solution.data(), initial_solution.size()); } - auto solution = cuopt::linear_programming::solve_mip(op_problem, mip_settings); } else { auto& lp_settings = settings.get_pdlp_settings(); if (initial_solution.size() > 0) { lp_settings.set_initial_primal_solution(initial_solution.data(), initial_solution.size()); } - auto solution = cuopt::linear_programming::solve_lp(op_problem, lp_settings); + } + } catch (const std::exception& e) { + auto log = dummy_logger(settings); + CUOPT_LOG_ERROR("Error: %s", e.what()); + return -1; + } + + try { + if (is_mip) { + auto& mip_settings = settings.get_mip_settings(); + auto solution = cuopt::linear_programming::solve_mip(op_problem, mip_settings); + } else { + auto& lp_settings = settings.get_pdlp_settings(); + auto solution = cuopt::linear_programming::solve_lp(op_problem, lp_settings); } } catch (const std::exception& e) { CUOPT_LOG_ERROR("Error: %s", e.what()); return -1; } + return 0; } diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index d6868b7d3..f67a36174 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -14,7 +14,7 @@ # limitations under the License. set(UTIL_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/utilities/seed_generator.cu - ${CMAKE_CURRENT_SOURCE_DIR}/utilities/logger_helper.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/logger.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/version_info.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/timestamp_utils.cpp) diff --git a/cpp/src/dual_simplex/logger.hpp b/cpp/src/dual_simplex/logger.hpp index 11d8ff936..ac11ce562 100644 --- a/cpp/src/dual_simplex/logger.hpp +++ b/cpp/src/dual_simplex/logger.hpp @@ -18,7 +18,7 @@ #pragma once #ifdef CUOPT_LOG_ACTIVE_LEVEL -#include +#include #endif #include diff --git a/cpp/src/linear_programming/cuopt_c.cpp b/cpp/src/linear_programming/cuopt_c.cpp index df1fa1a4b..559b9e25b 100644 --- a/cpp/src/linear_programming/cuopt_c.cpp +++ b/cpp/src/linear_programming/cuopt_c.cpp @@ -20,8 +20,8 @@ #include #include #include -#include #include +#include #include diff --git a/cpp/src/linear_programming/optimization_problem.cu b/cpp/src/linear_programming/optimization_problem.cu index bcd2f9c4a..4b717eca2 100644 --- a/cpp/src/linear_programming/optimization_problem.cu +++ b/cpp/src/linear_programming/optimization_problem.cu @@ -16,8 +16,8 @@ */ #include -#include #include +#include #include #include diff --git a/cpp/src/linear_programming/solve.cu b/cpp/src/linear_programming/solve.cu index 2fb5d047e..378ea5950 100644 --- a/cpp/src/linear_programming/solve.cu +++ b/cpp/src/linear_programming/solve.cu @@ -20,9 +20,9 @@ #include #include #include -#include #include #include +#include #include #include diff --git a/cpp/src/linear_programming/solver_settings.cu b/cpp/src/linear_programming/solver_settings.cu index d4b4388af..3188d7556 100644 --- a/cpp/src/linear_programming/solver_settings.cu +++ b/cpp/src/linear_programming/solver_settings.cu @@ -18,10 +18,10 @@ #include #include #include -#include #include #include #include +#include #include diff --git a/cpp/src/linear_programming/solver_solution.cu b/cpp/src/linear_programming/solver_solution.cu index b611bce6d..22c2262e6 100644 --- a/cpp/src/linear_programming/solver_solution.cu +++ b/cpp/src/linear_programming/solver_solution.cu @@ -16,9 +16,9 @@ */ #include -#include #include #include +#include #include #include diff --git a/cpp/src/linear_programming/utilities/logger_init.hpp b/cpp/src/linear_programming/utilities/logger_init.hpp deleted file mode 100644 index 093c4ae1c..000000000 --- a/cpp/src/linear_programming/utilities/logger_init.hpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights - * reserved. SPDX-License-Identifier: Apache-2.0 - * - * 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 - -namespace cuopt::linear_programming { -class init_logger_t { - public: - init_logger_t(std::string log_file, bool log_to_console = true) - { - write_log_to_console = log_to_console; - log_file_name = log_file; - - if (!write_log_to_console) { - // popback the default sink - cuopt::default_logger().sinks().pop_back(); - } - - if (not log_file_name.empty()) { - // TODO save the defaul sink and restore it - cuopt::default_logger().sinks().push_back( - std::make_shared(log_file, true)); -#if CUOPT_LOG_ACTIVE_LEVEL >= RAPIDS_LOGGER_LOG_LEVEL_INFO - cuopt::default_logger().set_pattern("%v"); -#else - cuopt::default_logger().set_pattern(cuopt::default_pattern()); -#endif - cuopt::default_logger().flush_on(rapids_logger::level_enum::debug); - } - } - ~init_logger_t() { cuopt::reset_default_logger(); } - - private: - std::string log_file_name; - bool write_log_to_console; -}; -} // namespace cuopt::linear_programming diff --git a/cpp/src/math_optimization/solution_writer.cu b/cpp/src/math_optimization/solution_writer.cu index 50e2e2dbb..f818c70c0 100644 --- a/cpp/src/math_optimization/solution_writer.cu +++ b/cpp/src/math_optimization/solution_writer.cu @@ -15,8 +15,8 @@ * limitations under the License. */ -#include #include +#include #include "solution_writer.hpp" #include diff --git a/cpp/src/math_optimization/solver_settings.cu b/cpp/src/math_optimization/solver_settings.cu index 42faaae94..e689b71a8 100644 --- a/cpp/src/math_optimization/solver_settings.cu +++ b/cpp/src/math_optimization/solver_settings.cu @@ -16,8 +16,8 @@ */ #include -#include #include +#include namespace cuopt::linear_programming { diff --git a/cpp/src/mip/logger.cuh b/cpp/src/mip/logger.cuh index 8edffdfd1..685c91481 100644 --- a/cpp/src/mip/logger.cuh +++ b/cpp/src/mip/logger.cuh @@ -17,7 +17,7 @@ #pragma once -#include +#include namespace cuopt::linear_programming::detail { diff --git a/cpp/src/mip/logger.hpp b/cpp/src/mip/logger.hpp index 8f1454bc2..dc6b51905 100644 --- a/cpp/src/mip/logger.hpp +++ b/cpp/src/mip/logger.hpp @@ -17,4 +17,4 @@ #pragma once -#include +#include diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 28143a188..834d03da7 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -16,10 +16,10 @@ */ #include -#include #include #include #include +#include #include #include diff --git a/cpp/src/mip/solve.cu b/cpp/src/mip/solve.cu index d8dc783c3..44ab24a6a 100644 --- a/cpp/src/mip/solve.cu +++ b/cpp/src/mip/solve.cu @@ -28,9 +28,9 @@ #include #include #include -#include #include #include +#include #include #include diff --git a/cpp/src/mip/solver_solution.cu b/cpp/src/mip/solver_solution.cu index f1f00dc80..a15f75490 100644 --- a/cpp/src/mip/solver_solution.cu +++ b/cpp/src/mip/solver_solution.cu @@ -16,8 +16,8 @@ */ #include -#include #include +#include #include #include diff --git a/cpp/src/routing/solve.cu b/cpp/src/routing/solve.cu index 11fedcf18..2deb5fce1 100644 --- a/cpp/src/routing/solve.cu +++ b/cpp/src/routing/solve.cu @@ -15,9 +15,9 @@ * limitations under the License. */ -#include #include #include +#include namespace cuopt { namespace routing { diff --git a/cpp/include/cuopt/logger.hpp b/cpp/src/utilities/logger.cpp similarity index 53% rename from cpp/include/cuopt/logger.hpp rename to cpp/src/utilities/logger.cpp index f8f4e200e..c4752d9ff 100644 --- a/cpp/include/cuopt/logger.hpp +++ b/cpp/src/utilities/logger.cpp @@ -15,13 +15,62 @@ * limitations under the License. */ -#pragma once +#include +#include -#include +namespace cuopt { -#include +struct buffered_entry { + rapids_logger::level_enum level; + std::string msg; +}; -namespace cuopt { +// Buffer to store log messages +class log_buffer { + public: + log_buffer() = default; + ~log_buffer() = default; + + void log(rapids_logger::level_enum lvl, const char* msg) + { + std::lock_guard lock(mutex); + if (!msg) return; + std::string str(msg); + + if (!str.empty() && str.back() == '\n') { str.pop_back(); } + messages.push_back({lvl, std::move(str)}); + } + + size_t size() const + { + std::lock_guard lock(mutex); + return messages.size(); + } + + std::vector drain_all() + { + std::lock_guard lock(mutex); + std::vector out; + out.swap(messages); + return out; + } + + std::vector messages; + mutable std::mutex mutex; +}; + +log_buffer& global_log_buffer() +{ + static log_buffer buffer; + return buffer; +} + +// Callback function for the buffer sink +static void buffer_log_callback(int lvl, const char* msg) +{ + // store level with message; actual filtering happens at logger time + global_log_buffer().log(static_cast(lvl), msg); +} /** * @brief Returns the default sink for the global logger. @@ -31,7 +80,11 @@ namespace cuopt { * * @return sink_ptr The sink to use */ -rapids_logger::sink_ptr default_sink(); +rapids_logger::sink_ptr default_sink() +{ + return std::make_shared(buffer_log_callback); +} + /** * @brief Returns the default log pattern for the global logger. * @@ -63,12 +116,7 @@ inline rapids_logger::level_enum default_level() #endif } -/** - * @brief Get the default logger. - * - * @return logger& The default logger - */ -inline rapids_logger::logger& default_logger() +rapids_logger::logger& default_logger() { static rapids_logger::logger logger_ = [] { rapids_logger::logger logger_{"CUOPT", {default_sink()}}; @@ -82,15 +130,11 @@ inline rapids_logger::logger& default_logger() return logger_; }(); + return logger_; } -/** - * @brief Reset the default logger to the default settings. - * This is needed when we are running multiple tests and each test has different logger settings - * and we need to reset the logger to the default settings before each test. - */ -inline void reset_default_logger() +void reset_default_logger() { default_logger().sinks().clear(); default_logger().sinks().push_back(default_sink()); @@ -103,4 +147,35 @@ inline void reset_default_logger() default_logger().flush_on(rapids_logger::level_enum::debug); } +init_logger_t::init_logger_t(std::string log_file, bool log_to_console) +{ + // until this function is called, the default sink is the buffer sink + cuopt::default_logger().sinks().clear(); + + // re-initialize sinks + if (log_to_console) { + cuopt::default_logger().sinks().push_back( + std::make_shared(std::cout)); + } + if (!log_file.empty()) { + cuopt::default_logger().sinks().push_back( + std::make_shared(log_file, true)); + cuopt::default_logger().flush_on(rapids_logger::level_enum::debug); + } + +#if CUOPT_LOG_ACTIVE_LEVEL >= RAPIDS_LOGGER_LOG_LEVEL_INFO + cuopt::default_logger().set_pattern("%v"); +#else + cuopt::default_logger().set_pattern(cuopt::default_pattern()); +#endif + + // Extract messages from the global buffer and log to the default logger + auto buffered_messages = global_log_buffer().drain_all(); + for (const auto& entry : buffered_messages) { + cuopt::default_logger().log(entry.level, entry.msg.c_str()); + } +} + +init_logger_t::~init_logger_t() { cuopt::reset_default_logger(); } + } // namespace cuopt diff --git a/cpp/src/utilities/logger_helper.cpp b/cpp/src/utilities/logger.hpp similarity index 53% rename from cpp/src/utilities/logger_helper.cpp rename to cpp/src/utilities/logger.hpp index d6f6ebb41..4a5dbdafd 100644 --- a/cpp/src/utilities/logger_helper.cpp +++ b/cpp/src/utilities/logger.hpp @@ -15,20 +15,39 @@ * limitations under the License. */ -#include +#pragma once + +#include + +#include #include +#include +#include +#include +#include namespace cuopt { -rapids_logger::sink_ptr default_sink() -{ - auto* filename = std::getenv("CUOPT_DEBUG_LOG_FILE"); - return (filename == nullptr) - ? static_cast( - std::make_shared(std::cout)) - : static_cast( - std::make_shared(filename, true)); -} +/** + * @brief Get the default logger. + * + * @return logger& The default logger + */ +rapids_logger::logger& default_logger(); + +/** + * @brief Reset the default logger to the default settings. + * This is needed when we are running multiple tests and each test has different logger settings + * and we need to reset the logger to the default settings before each test. + */ +void reset_default_logger(); + +class init_logger_t { + public: + init_logger_t(std::string log_file, bool log_to_console); + + ~init_logger_t(); +}; } // namespace cuopt diff --git a/cpp/src/utilities/version_info.cpp b/cpp/src/utilities/version_info.cpp index 902495a48..e62c639fa 100644 --- a/cpp/src/utilities/version_info.cpp +++ b/cpp/src/utilities/version_info.cpp @@ -18,8 +18,8 @@ #include -#include #include +#include #include #include diff --git a/cpp/tests/utilities/test_cli.cpp b/cpp/tests/utilities/test_cli.cpp index ba8a31b9a..5bc87d707 100644 --- a/cpp/tests/utilities/test_cli.cpp +++ b/cpp/tests/utilities/test_cli.cpp @@ -143,6 +143,7 @@ TEST_F(cli_test_t, invalid_mps_file) invalid.close(); auto output = run_cli({invalid_file.string()}); + std::cout << "Output: " << output << std::endl; EXPECT_TRUE(output.find("error") != std::string::npos || output.find("Error") != std::string::npos); }