Skip to content

Conversation

@rg20
Copy link
Contributor

@rg20 rg20 commented Oct 24, 2025

Description

Prior to this PR, some logging info relating to solver parameters was shown in stdout. This is because we initialize the logger to stdout and we do not have apriori information whether the corresponding parameter would be set or not. In this PR, I am using the callback logger along with a log buffer to store all the logging info until the appropriate parameters and the loggers are initialized.

Issue

Closes #187

Checklist

  • I am familiar with the Contributing Guidelines.
  • Testing
    • New or existing tests cover these changes
    • Added tests
    • Created an issue to follow-up
    • NA
  • Documentation
    • The documentation is up to date with these changes
    • Added new documentation
    • NA

Summary by CodeRabbit

  • New Features

    • Added log buffering to capture early messages and replay them when logging initializes.
  • Refactor

    • Unified and simplified logging API with a scoped initializer for consistent startup/shutdown.
  • Chores

    • Switched the codebase to the revised logging module across components.
  • Bug Fixes / Diagnostics

    • Ensured logging is available on error paths and added extra CLI error handling to surface failures.
  • Tests

    • Enhanced CLI test output with additional diagnostic printing.

@rg20 rg20 requested review from a team as code owners October 24, 2025 18:15
@rg20 rg20 requested review from akifcorduk, aliceb-nv and tmckayus and removed request for a team October 24, 2025 18:15
@copy-pr-bot
Copy link

copy-pr-bot bot commented Oct 24, 2025

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@rg20 rg20 added bug Something isn't working non-breaking Introduces a non-breaking change labels Oct 24, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 24, 2025

Walkthrough

Swapped many includes from cuopt/logger.hpp to utilities/logger.hpp; added a buffered logging implementation and RAII initializer in cpp/src/utilities (logger.hpp/cpp); removed a linear_programming-specific logger_init header; updated CMake to compile the new logger source; small CLI/test diagnostic and solver-call error-handling changes.

Changes

Cohort / File(s) Summary
Header swaps (cuopt → utilities)
benchmarks/linear_programming/cuopt/run_mip.cpp, cpp/cuopt_cli.cpp, cpp/src/dual_simplex/logger.hpp, cpp/src/linear_programming/cuopt_c.cpp, cpp/src/linear_programming/optimization_problem.cu, cpp/src/linear_programming/solve.cu, cpp/src/linear_programming/solver_settings.cu, cpp/src/linear_programming/solver_solution.cu, cpp/src/math_optimization/solution_writer.cu, cpp/src/math_optimization/solver_settings.cu, cpp/src/mip/logger.cuh, cpp/src/mip/logger.hpp, cpp/src/mip/presolve/third_party_presolve.cpp, cpp/src/mip/solve.cu, cpp/src/mip/solver_solution.cu, cpp/src/routing/solve.cu, cpp/src/utilities/version_info.cpp, cpp/tests/utilities/test_cli.cpp
Replaced #include <cuopt/logger.hpp> or #include <linear_programming/utilities/logger_init.hpp> with #include <utilities/logger.hpp>; added a small dummy/logger-init helper and additional solver error-catch in cpp/cuopt_cli.cpp; added an extra stdout diagnostic in a test. No other logic changes.
Utilities logger implementation
cpp/src/utilities/logger.cpp
Added buffered logging (buffered_entry, log_buffer, global_log_buffer, buffer_log_callback), changed default sink to buffer callback, implemented init_logger_t to configure sinks and replay buffered logs, and changed default_logger() / reset_default_logger() linkage/behavior.
Utilities logger header
cpp/src/utilities/logger.hpp
New/updated public declarations: rapids_logger::logger& default_logger(), void reset_default_logger(), and RAII cuopt::init_logger_t (ctor/dtor); header reorganized and includes updated.
Removed LP-specific init
cpp/src/linear_programming/utilities/logger_init.hpp
Deleted the init_logger_t RAII helper that previously lived under cuopt::linear_programming.
Build config update
cpp/src/CMakeLists.txt
UTIL_SRC_FILES now references utilities/logger.cpp instead of utilities/logger_helper.cpp.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

  • Inspect cpp/src/utilities/logger.cpp and cpp/src/utilities/logger.hpp for thread-safety (mutex use), correct replay ordering of buffered messages, and static lifetime interactions with rapids_logger.
  • Verify callers compile against the changed default_logger() / reset_default_logger() signatures and that no ODR/linkage issues arise.
  • Confirm removal of linear_programming/utilities/logger_init.hpp did not leave dangling includes/usages.
  • Review cpp/cuopt_cli.cpp changes for potential unintended console output when LOG_TO_CONSOLE is disabled.

Poem

🐰
I hid soft logs in earthen nooks, a furtive, fuzzy heap,
I stored each chirp and clatter safe until the world woke up.
Then with a hop I spilled them out, in tidy ordered peeps—
Replayed the tales I buffered, nibbling carrots from my cup. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.29% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Fix bug in logger when log_to_console is set to false" clearly and specifically summarizes the primary change in the changeset. The title directly corresponds to the main objective of implementing a log buffering mechanism to prevent premature stdout output when the LOG_TO_CONSOLE setting is false. The title is concise, descriptive, and avoids vague or generic language, making it suitable for scanning historical commits.
Linked Issues Check ✅ Passed The pull request implements the key requirements from issue #187 by introducing a log buffering mechanism that captures logging output via buffer_log_callback and log_buffer until the logger is properly configured with the correct LOG_TO_CONSOLE setting. The refactored init_logger_t class drains buffered messages and reconfigures sinks based on the log-to-console parameter, effectively preventing parameter-change log messages from being emitted to stdout prematurely. The widespread replacement of cuopt/logger.hpp with utilities/logger.hpp across multiple compilation units ensures the new buffering infrastructure is consistently applied throughout the codebase.
Out of Scope Changes Check ✅ Passed The vast majority of changes are directly in-scope with the objectives of fixing the logger bug and implementing log buffering. The refactoring of error handling in cpp/cuopt_cli.cpp with dummy_logger and enhanced try-catch blocks aligns with ensuring proper logger initialization in error paths, supporting the primary objective. However, there is one minor out-of-scope addition: a debug output line (std::cout << "Output: " << output << std::endl;) added to cpp/tests/utilities/test_cli.cpp in the invalid_mips_file test case, which is unrelated to the logging infrastructure fix but does not affect test functionality.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rg20 rg20 added this to the 25.12 milestone Oct 24, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
cpp/src/utilities/logger.cpp (2)

23-52: Consider making the messages vector private.

The messages member at line 50 is public, which breaks encapsulation. While the accessor methods properly use the mutex, direct access to messages would bypass thread safety.

Apply this diff:

-  std::vector<std::string> messages;
-  mutable std::mutex mutex;
+ private:
+  std::vector<std::string> messages;
+  mutable std::mutex mutex;

Note: The buffer could grow unbounded if logger initialization is significantly delayed. Consider adding a maximum buffer size or warning if this becomes an issue in practice.


142-175: Consider protecting against concurrent initialization.

The init_logger_t constructor and destructor modify the global default_logger() sinks without synchronization. If multiple instances are created concurrently (e.g., in a multi-threaded test environment), race conditions could occur:

  • Line 146: pop_back() could be called twice, causing undefined behavior if the sinks vector becomes empty
  • Lines 150-157: Multiple threads could add duplicate sinks
  • Line 175: Destructor resets logger, potentially interfering with other active instances

While typical usage (one solver per process) may not trigger this, consider adding:

  1. A static mutex to protect initialization
  2. A reference count to prevent premature reset in the destructor
  3. Documentation noting that init_logger_t should not be used concurrently

Based on learnings: The PR objectives mention this is for initialization at solver setup, so concurrent usage may be unlikely in practice.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a814d5 and fcb484b.

📒 Files selected for processing (21)
  • benchmarks/linear_programming/cuopt/run_mip.cpp (1 hunks)
  • cpp/cuopt_cli.cpp (1 hunks)
  • cpp/src/CMakeLists.txt (1 hunks)
  • cpp/src/dual_simplex/logger.hpp (1 hunks)
  • cpp/src/linear_programming/cuopt_c.cpp (1 hunks)
  • cpp/src/linear_programming/optimization_problem.cu (1 hunks)
  • cpp/src/linear_programming/solve.cu (1 hunks)
  • cpp/src/linear_programming/solver_settings.cu (1 hunks)
  • cpp/src/linear_programming/solver_solution.cu (1 hunks)
  • cpp/src/linear_programming/utilities/logger_init.hpp (0 hunks)
  • cpp/src/math_optimization/solution_writer.cu (1 hunks)
  • cpp/src/math_optimization/solver_settings.cu (1 hunks)
  • cpp/src/mip/logger.cuh (1 hunks)
  • cpp/src/mip/logger.hpp (1 hunks)
  • cpp/src/mip/presolve/third_party_presolve.cpp (1 hunks)
  • cpp/src/mip/solve.cu (1 hunks)
  • cpp/src/mip/solver_solution.cu (1 hunks)
  • cpp/src/routing/solve.cu (1 hunks)
  • cpp/src/utilities/logger.cpp (5 hunks)
  • cpp/src/utilities/logger.hpp (1 hunks)
  • cpp/src/utilities/version_info.cpp (1 hunks)
💤 Files with no reviewable changes (1)
  • cpp/src/linear_programming/utilities/logger_init.hpp
🧰 Additional context used
🧬 Code graph analysis (1)
cpp/src/utilities/logger.hpp (1)
cpp/src/dual_simplex/logger.hpp (2)
  • log_file (43-48)
  • log_file (56-59)
🪛 Clang (14.0.6)
cpp/src/utilities/logger.hpp

[error] 20-20: 'cuopt/logger_macros.hpp' file not found

(clang-diagnostic-error)

cpp/src/mip/logger.hpp

[error] 20-20: 'utilities/logger.hpp' file not found

(clang-diagnostic-error)

cpp/src/utilities/logger.cpp

[error] 18-18: 'utilities/logger.hpp' file not found

(clang-diagnostic-error)

🔇 Additional comments (20)
cpp/cuopt_cli.cpp (1)

22-22: LGTM! Header migration aligns with logging consolidation.

The include path update from cuopt/logger.hpp to utilities/logger.hpp is consistent with the project-wide refactoring to centralize logging infrastructure and fix the early initialization issue described in the PR objectives.

cpp/src/linear_programming/solver_solution.cu (1)

21-21: LGTM! Logging header migration is correct.

The header path update aligns with the centralized logging infrastructure introduced in this PR.

cpp/src/math_optimization/solver_settings.cu (1)

20-20: LGTM! Header migration enables proper parameter logging.

This header update is particularly important for the PR objective, as this file logs parameter changes (lines 148, 166, 181, 195, etc.). Migrating to utilities/logger.hpp ensures these parameter logs are buffered until the logger is properly initialized, preventing premature stdout output when LOG_TO_CONSOLE is false.

cpp/src/linear_programming/optimization_problem.cu (1)

20-20: LGTM! Header path update is correct.

The migration to utilities/logger.hpp is consistent with the project-wide logging consolidation effort.

cpp/src/utilities/version_info.cpp (1)

22-22: LGTM! Header migration fixes early version logging.

The header update is correct. Since print_version_info() is called early during solver initialization, this change ensures version information logs are properly buffered and respect the LOG_TO_CONSOLE setting.

benchmarks/linear_programming/cuopt/run_mip.cpp (1)

26-26: LGTM! Benchmark code updated consistently.

The header migration is correct and aligns with the logging infrastructure changes across the project.

cpp/src/linear_programming/solve.cu (1)

25-25: LGTM! Logger initialization header consolidation is correct.

The change from linear_programming/utilities/logger_init.hpp to utilities/logger.hpp consolidates the logger initialization API. Line 799 shows init_logger_t being instantiated with settings, which is the key to ensuring logs are properly initialized before parameter logging occurs.

cpp/src/math_optimization/solution_writer.cu (1)

19-19: LGTM! Header migration complete.

The include path update is correct and completes the consistent migration to utilities/logger.hpp across the codebase.

cpp/src/routing/solve.cu (1)

20-20: LGTM! Consistent header migration.

The include path update aligns with the repository-wide logging infrastructure consolidation.

cpp/src/mip/logger.cuh (1)

20-20: LGTM! Consistent header migration.

The include path update aligns with the repository-wide logging infrastructure consolidation.

cpp/src/CMakeLists.txt (1)

17-17: LGTM! Build configuration updated for new logger implementation.

The change from logger_helper.cpp to logger.cpp reflects the logger infrastructure refactoring that introduces the log buffering mechanism.

cpp/src/mip/presolve/third_party_presolve.cpp (1)

22-22: LGTM! Consistent header migration.

The include path update aligns with the repository-wide logging infrastructure consolidation.

cpp/src/mip/logger.hpp (1)

20-20: LGTM! Consistent header migration.

The include path update aligns with the repository-wide logging infrastructure consolidation. The static analysis warning about the missing file is likely a false positive since the CMakeLists.txt updates reference the new logger implementation files.

Please verify that the codebase compiles successfully with these header changes.

cpp/src/mip/solver_solution.cu (1)

20-20: LGTM! Consistent header migration.

The include path update aligns with the repository-wide logging infrastructure consolidation.

cpp/src/linear_programming/solver_settings.cu (1)

24-24: LGTM! Consistent header migration.

The include path update aligns with the repository-wide logging infrastructure consolidation.

cpp/src/dual_simplex/logger.hpp (1)

21-21: LGTM! Consistent header migration.

The include path update aligns with the repository-wide logging infrastructure consolidation. Note that this file conditionally includes the logger only when CUOPT_LOG_ACTIVE_LEVEL is defined, which is appropriate for build-time log level control.

cpp/src/mip/solve.cu (1)

32-32: LGTM: Include path updated to new logger infrastructure.

The change from linear_programming/utilities/logger_init.hpp to utilities/logger.hpp aligns with the centralized logging refactor.

cpp/src/linear_programming/cuopt_c.cpp (1)

24-24: LGTM: Include path updated to new logger module.

The change aligns with the project-wide logger refactor to use utilities/logger.hpp.

cpp/src/utilities/logger.hpp (1)

18-51: LGTM: Clean public API for logger management.

The header provides a minimal, well-designed public interface with RAII-based logger initialization. The init_logger_t class will ensure proper cleanup when it goes out of scope.

cpp/src/utilities/logger.cpp (1)

142-173: Verify behavior when both log_to_console is false and log_file is empty.

The constructor unconditionally removes the default buffer sink (line 146), then conditionally adds console and file sinks. If both log_to_console is false and log_file is empty, no sink will be added, causing subsequent log messages to be discarded silently.

Please verify this is the intended behavior. If not, consider either:

  1. Keeping at least one sink active, or
  2. Adding a comment documenting that this configuration silently discards logs

Also address the commented-out code (lines 144-147) and TODO (line 154) that suggest this logic is still under consideration, and remove the commented debug output at line 168.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
cpp/src/utilities/logger.cpp (1)

163-169: Current flush replays everything as info and can drop messages.

Given the earlier refactor, adopt drain_prefix + level-preserving replay to meet correctness and avoid loss.

🧹 Nitpick comments (4)
cpp/src/utilities/logger.cpp (4)

67-78: Update stale docstring and change-of-behavior notice.

Comment says default sink uses CUOPT_DEBUG_LOG_FILE or stderr; code now always uses a callback sink. Update docs to reflect buffering approach. Also note console sink uses stdout (ostream_sink_mt to std::cout) vs historical stderr.

- * If the environment variable `CUOPT_DEBUG_LOG_FILE` is defined, the default sink is a sink to that
- * file. Otherwise, the default is to dump to stderr.
+ * Prior to explicit initialization, the default sink is a callback sink that buffers messages
+ * in-memory (no console/file output). After init, messages are routed to configured sinks.

129-140: Reset logic is OK, but consider removing callback sink by type, not position.

If sinks were reordered elsewhere, push_back(default_sink()) could duplicate callback sinks. Not urgent but safer to rebuild sink list deterministically.


171-172: Destructor resets to buffered mode; confirm lifecycle.

Resetting the global logger in ~init_logger_t() may surprise downstream code if the RAII object goes out of scope early. Consider making reset explicit or documenting that init_logger_t must live for process lifetime.

Do you expect init_logger_t to be created once during startup and live until shutdown?


147-155: Console to stdout vs stderr.

Historically comments mention stderr; code uses std::cout. Confirm product expectation. If parity with prior behavior is desired, use std::cerr.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fcb484b and 8e94a8c.

📒 Files selected for processing (1)
  • cpp/src/utilities/logger.cpp (5 hunks)
🧰 Additional context used
🪛 Clang (14.0.6)
cpp/src/utilities/logger.cpp

[error] 18-18: 'utilities/logger.hpp' file not found

(clang-diagnostic-error)

🔇 Additional comments (2)
cpp/src/utilities/logger.cpp (2)

111-127: Default logger setup looks fine.

Pattern/level wiring via compile-time macros is consistent; returns a stable singleton.


18-18: ****

The header utilities/logger.hpp exists at cpp/src/utilities/logger.hpp and is properly configured in the build system. The source file cpp/src/utilities/logger.cpp is compiled as part of the cuopt target (added via cpp/src/CMakeLists.txt line 17), which has ${CMAKE_CURRENT_SOURCE_DIR}/src in its PRIVATE include directories. When a source file is compiled as part of a target, it has access to that target's PRIVATE includes, so the angle-bracket include #include <utilities/logger.hpp> resolves correctly. No build error exists.

Likely an incorrect or invalid review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
cpp/src/utilities/logger.cpp (1)

150-179: Critical initialization race conditions remain unresolved from previous review.

The constructor still has the race conditions identified in the previous review:

  1. Zero-sink window (line 154): clear() removes all sinks, including the buffer sink. Between the clear and adding new sinks (lines 158-166), any logs are dropped.
  2. No serialization: Multiple concurrent init_logger_t constructions can race. The class is not a singleton (per logger.hpp lines 46–51), so concurrent instantiation is possible.
  3. Buffer sink removal: When sinks are cleared at line 154, the buffer callback sink is also removed. Any logs arriving between clear and drain (line 175) are lost because there's no sink to receive them.

The previous review provided a detailed solution involving:

  • A static mutex to serialize logger reconfiguration
  • Adding new sinks before removing the buffer sink
  • Removing only the callback sink (not clearing all sinks)

This approach ensures no log-loss window exists.

Apply a minimal fix to prevent the zero-sink window:

 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
+  auto& logger = cuopt::default_logger();
+  
+  // Add new sinks first to avoid zero-sink window
   if (log_to_console) {
-    cuopt::default_logger().sinks().push_back(
-      std::make_shared<rapids_logger::ostream_sink_mt>(std::cout));
+    logger.sinks().push_back(std::make_shared<rapids_logger::ostream_sink_mt>(std::cout));
   }
   if (!log_file.empty()) {
-    cuopt::default_logger().sinks().push_back(
-      std::make_shared<rapids_logger::basic_file_sink_mt>(log_file, true));
-    cuopt::default_logger().flush_on(rapids_logger::level_enum::debug);
+    logger.sinks().push_back(std::make_shared<rapids_logger::basic_file_sink_mt>(log_file, true));
+    logger.flush_on(rapids_logger::level_enum::debug);
   }
 
 #if CUOPT_LOG_ACTIVE_LEVEL >= RAPIDS_LOGGER_LOG_LEVEL_INFO
-  cuopt::default_logger().set_pattern("%v");
+  logger.set_pattern("%v");
 #else
-  cuopt::default_logger().set_pattern(cuopt::default_pattern());
+  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());
+    logger.log(entry.level, entry.msg.c_str());
   }
+  
+  // Remove buffer sink only after draining and with new sinks in place
+  auto& sinks = logger.sinks();
+  sinks.erase(std::remove_if(sinks.begin(), sinks.end(),
+    [](const rapids_logger::sink_ptr& sp) {
+      return std::dynamic_pointer_cast<rapids_logger::callback_sink_mt>(sp) != nullptr;
+    }),
+    sinks.end());
 }

Note: For full thread-safety with concurrent init_logger_t constructions, add a static mutex as detailed in the previous review.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e94a8c and a4afa77.

📒 Files selected for processing (1)
  • cpp/src/utilities/logger.cpp (5 hunks)
🧰 Additional context used
🪛 Clang (14.0.6)
cpp/src/utilities/logger.cpp

[error] 18-18: 'utilities/logger.hpp' file not found

(clang-diagnostic-error)

🔇 Additional comments (2)
cpp/src/utilities/logger.cpp (2)

23-60: Excellent implementation of buffered logging with level preservation.

The log_buffer class now correctly addresses the concerns from previous reviews:

  • buffered_entry struct preserves both level and message
  • log() method null-guards msg and stores the severity
  • drain_all() provides atomic swap semantics, eliminating the race between get/clear
  • Thread-safe operations with proper mutex usage

68-73: Callback correctly preserves log severity.

The callback now properly casts the level and forwards it to the buffer, addressing the previous review concern about dropped severity information.

@rg20
Copy link
Contributor Author

rg20 commented Oct 27, 2025

/ok to test b0c872e

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
cpp/src/utilities/logger.cpp (1)

153-164: Zero-sink window can drop logs; consider adding sinks before clearing.

Between clear() (line 153) and adding the console/file sinks (lines 156–164), the logger has no sinks. Any log emitted by another thread during this brief window will be silently dropped.

While this may be acceptable if init_logger_t is only constructed once during single-threaded startup, a safer approach is to add the new sinks before clearing the old ones.

Example safer order:

-  cuopt::default_logger().sinks().clear();
-
-  // re-initialize sinks
+  // re-initialize sinks
+  auto& logger = cuopt::default_logger();
+  
+  // Add new sinks first to avoid a zero-sink window
   if (log_to_console) {
-    cuopt::default_logger().sinks().push_back(
+    logger.sinks().push_back(
       std::make_shared<rapids_logger::ostream_sink_mt>(std::cout));
   }
   if (!log_file.empty()) {
-    cuopt::default_logger().sinks().push_back(
+    logger.sinks().push_back(
       std::make_shared<rapids_logger::basic_file_sink_mt>(log_file, true));
-    cuopt::default_logger().flush_on(rapids_logger::level_enum::debug);
+    logger.flush_on(rapids_logger::level_enum::debug);
   }
+  
+  // Now remove the buffer sink after new sinks are in place
+  auto& sinks = logger.sinks();
+  sinks.erase(
+    std::remove_if(sinks.begin(), sinks.end(),
+      [](const auto& sp) {
+        return std::dynamic_pointer_cast<rapids_logger::callback_sink_mt>(sp) != nullptr;
+      }),
+    sinks.end());
🧹 Nitpick comments (1)
cpp/src/utilities/logger.cpp (1)

58-59: Consider making data members private.

Public messages and mutex break encapsulation. While the current API doesn't expose direct mutation, making them private would prevent future misuse.

Apply this diff:

+ private:
   std::vector<buffered_entry> messages;
   mutable std::mutex mutex;
 };
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a4afa77 and b0c872e.

📒 Files selected for processing (1)
  • cpp/src/utilities/logger.cpp (5 hunks)
🧰 Additional context used
🪛 Clang (14.0.6)
cpp/src/utilities/logger.cpp

[error] 18-18: 'utilities/logger.hpp' file not found

(clang-diagnostic-error)

🔇 Additional comments (6)
cpp/src/utilities/logger.cpp (6)

23-26: LGTM—severity preservation addressed.

The buffered_entry struct correctly stores both level and message, addressing the previous concern about losing severity information during buffering.


34-56: Excellent—atomic drain eliminates the get+clear race.

The implementation correctly addresses previous concerns:

  • log() preserves severity and null-guards the message.
  • drain_all() atomically extracts messages via swap(), preventing the race between separate get/clear calls.

62-73: LGTM—callback preserves severity.

The singleton pattern is thread-safe, and buffer_log_callback correctly casts and forwards the level to the buffer, addressing the previous "FIXME: check for levels?" comment.


172-176: LGTM—buffered messages replayed with original severity.

The drain-and-replay logic correctly uses entry.level to preserve each message's severity, addressing the prior concern about replaying everything as info.


179-179: LGTM—cleanup restores buffer sink.

The destructor correctly calls reset_default_logger() to reinstate the buffer sink, ensuring any subsequent logs are captured if the logger is used after init_logger_t goes out of scope.


150-177: Race condition in init_logger_t constructor when called concurrently.

If multiple threads invoke init_logger_t simultaneously (e.g., concurrent call_solve or call_batch_solve from Python/Cython), the unprotected calls to sinks().clear() and sinks().push_back() in the constructor will race, corrupting the logger's sink list or causing out-of-order message replay.

Although the codebase doesn't explicitly spawn multiple threads to call init_logger_t, the Cython interface exposes these functions to Python, where client code can invoke them concurrently. Protect the constructor body with a static mutex:

init_logger_t::init_logger_t(std::string log_file, bool log_to_console)
{
  static std::mutex init_mutex;
  std::lock_guard<std::mutex> lock(init_mutex);
  // ... rest of constructor
}

@rg20
Copy link
Contributor Author

rg20 commented Oct 27, 2025

/ok to test 78d3b97

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
cpp/tests/utilities/test_cli.cpp (1)

146-146: Consider removing diagnostic output.

This debug statement prints the CLI output to stdout during test execution. While harmless, it adds noise to test output and appears to be leftover development/debugging code.

Apply this diff if you'd like to remove it:

-  std::cout << "Output: " << output << std::endl;
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0c872e and 78d3b97.

📒 Files selected for processing (2)
  • cpp/cuopt_cli.cpp (7 hunks)
  • cpp/tests/utilities/test_cli.cpp (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
cpp/cuopt_cli.cpp (4)
cpp/src/utilities/logger.hpp (2)
  • init_logger_t (48-48)
  • init_logger_t (50-50)
cpp/src/linear_programming/solve.cu (5)
  • op_problem (938-938)
  • solve_lp (791-932)
  • solve_lp (791-795)
  • solve_lp (1005-1014)
  • solve_lp (1005-1010)
cpp/include/cuopt/linear_programming/mip/solver_settings.hpp (1)
  • initial_solution (62-64)
cpp/src/mip/solve.cu (5)
  • solution (82-82)
  • solve_mip (155-296)
  • solve_mip (155-156)
  • solve_mip (299-306)
  • solve_mip (299-302)
🔇 Additional comments (4)
cpp/cuopt_cli.cpp (4)

78-88: LGTM!

The dummy_logger helper is well-designed for early error paths. The RAII pattern ensures proper logger initialization and cleanup before error logging in edge cases.


109-111: LGTM!

Consistent use of dummy_logger in error paths ensures logging is properly configured before errors are emitted, even when normal initialization hasn't occurred yet.

Also applies to: 129-131, 165-168


138-144: Verify the logic for IP problems with relaxation.

When solve_relaxation is true for an IP (Integer Programming) problem, both is_mip and is_lp will be false. This causes the else branch (MIP solver) to execute at line 174, which may not be the intended behavior for a relaxation.

Should is_lp also handle the IP + relaxation case?

 const bool is_lp =
   op_problem.get_problem_category() == cuopt::linear_programming::problem_category_t::LP ||
-  (op_problem.get_problem_category() == cuopt::linear_programming::problem_category_t::MIP &&
-   solve_relaxation);
+  ((op_problem.get_problem_category() == cuopt::linear_programming::problem_category_t::MIP ||
+    op_problem.get_problem_category() == cuopt::linear_programming::problem_category_t::IP) &&
+   solve_relaxation);

153-168: LGTM!

Initial solution loading is properly wrapped in error handling with appropriate logger initialization before error emission.

@rg20
Copy link
Contributor Author

rg20 commented Oct 28, 2025

/ok to test e5cc7cd

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
cpp/cuopt_cli.cpp (1)

166-177: Consider using dummy_logger for consistency in error handling.

The error logging at line 175 doesn't use dummy_logger, unlike the error handling at lines 109, 129, and 161. If an exception escapes from solve_mip or solve_lp, their internal init_logger_t will have been destroyed during stack unwinding, so line 175 would use the default logger and potentially bypass the log_to_console setting.

Apply this diff for consistency:

   } catch (const std::exception& e) {
+    auto log = dummy_logger(settings);
     CUOPT_LOG_ERROR("Error: %s", e.what());
     return -1;
   }

Note: This is a minor consistency issue since exceptions escaping the solve functions are unexpected (they have internal error handling), but it would align with the error-handling pattern used throughout this file.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 78d3b97 and e5cc7cd.

📒 Files selected for processing (4)
  • cpp/cuopt_cli.cpp (6 hunks)
  • cpp/src/linear_programming/solve.cu (1 hunks)
  • cpp/src/mip/presolve/third_party_presolve.cpp (1 hunks)
  • cpp/src/mip/solve.cu (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • cpp/src/linear_programming/solve.cu
  • cpp/src/mip/presolve/third_party_presolve.cpp
🧰 Additional context used
🧬 Code graph analysis (1)
cpp/cuopt_cli.cpp (3)
cpp/src/utilities/logger.hpp (2)
  • init_logger_t (48-48)
  • init_logger_t (50-50)
cpp/src/linear_programming/solve.cu (5)
  • op_problem (937-937)
  • solve_lp (790-931)
  • solve_lp (790-794)
  • solve_lp (1004-1013)
  • solve_lp (1004-1009)
cpp/src/mip/solve.cu (5)
  • solution (83-83)
  • solve_mip (156-302)
  • solve_mip (156-157)
  • solve_mip (305-312)
  • solve_mip (305-308)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: wheel-build-cuopt-mps-parser / 13.0.1, 3.11, amd64, rockylinux8
  • GitHub Check: wheel-build-cuopt-mps-parser / 13.0.1, 3.10, amd64, rockylinux8
  • GitHub Check: wheel-build-cuopt-mps-parser / 13.0.1, 3.10, arm64, rockylinux8
  • GitHub Check: wheel-build-cuopt-mps-parser / 13.0.1, 3.12, amd64, rockylinux8
  • GitHub Check: wheel-build-cuopt-sh-client / 13.0.1, 3.10, amd64, rockylinux8
🔇 Additional comments (6)
cpp/src/mip/solve.cu (1)

33-33: LGTM! Correctly addresses the logging bug.

The switch from linear_programming/utilities/logger_init.hpp to utilities/logger.hpp enables the new RAII-based logger initialization at line 168, which properly configures the logger with settings.log_to_console before any log output occurs. This prevents parameter-change messages from being emitted to stdout when log_to_console is false, directly addressing the bug described in issue #187.

cpp/cuopt_cli.cpp (5)

78-88: Good approach for early error logging.

The dummy_logger helper ensures that error paths respect the log_to_console setting even before the main logger is initialized by the solver functions. The RAII pattern with init_logger_t is appropriate here.


104-112: LGTM: Proper logger initialization for parameter errors.

Using dummy_logger here ensures that parameter validation errors are logged according to the configured settings before the main solver initialization.


119-132: Appropriate error handling for parsing failures.

The use of dummy_logger at line 129 ensures parsing errors are logged with proper configuration. Note that line 120's CUOPT_LOG_INFO call relies on the buffered logging mechanism described in the PR (log buffer captures output until logger initialization).


137-140: Correct logic for LP relaxation handling.

The addition of !solve_relaxation properly ensures that when solving the LP relaxation of a MIP/IP problem, the LP solver is used instead of the MIP solver.


142-164: Well-structured error handling for initial solution loading.

The refactored code properly distinguishes between MIP and LP initial solution handling, and the try-catch block at lines 160-164 correctly uses dummy_logger to ensure error logging respects the configuration.

@rg20
Copy link
Contributor Author

rg20 commented Oct 29, 2025

/ok to test 5da3d88

@rg20
Copy link
Contributor Author

rg20 commented Oct 29, 2025

/merge

@rapids-bot rapids-bot bot merged commit c6bda57 into NVIDIA:main Oct 29, 2025
173 of 175 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working non-breaking Introduces a non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Parameter changes are sent to stdout even though CUOPT_LOG_TO_CONSOLE is false

4 participants