-
Notifications
You must be signed in to change notification settings - Fork 9
feat: Route solver output through Python logging #606
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…fault to the regular logger.
- stream_solver_log() in io.py: tails a log file in a background thread, forwards lines to logging.getLogger('flixopt.solver') at INFO level
- _disable_solver_console(): overrides solver console options (log_to_console/LogToConsole)
- capture_solver_log config setting (default False, enabled by presets)
- progress=False when capturing — eliminates tqdm progress bar noise
Known limitation (documented in docstring)
Gurobi may print a few lines (license banner, LP reading, parameter setting) directly to stdout before LogToConsole=0 takes effect. This is a Gurobi/linopy limitation. HiGHS
handles it cleanly.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a public Changes
Sequence Diagram(s)sequenceDiagram
participant App as Application
participant Solver as Solver (linopy / HiGHS)
participant LogFile as Log File
participant Tail as Tail Thread
participant Logger as flixopt.solver Logger
rect rgba(100,150,200,0.5)
Note over App,Logger: CONFIG.Solving.capture_solver_log = True
App->>App: Check CONFIG.Solving.capture_solver_log
App->>LogFile: Create/open log file (temp if needed)
App->>Tail: Start tailing thread
App->>Solver: Call model.solve(..., log_fn=LogFile.path, progress=False)
Solver->>LogFile: Write solver output lines
Tail->>LogFile: Read new lines
Tail->>Logger: Forward lines at INFO
end
rect rgba(150,150,150,0.5)
Note over App,Tail: Context exit
App->>Tail: Signal tail thread to stop
Tail->>LogFile: Drain remaining lines & stop
App->>LogFile: Remove temp file if created
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In `@flixopt/io.py`:
- Around line 1513-1621: stream_solver_log currently starts the tail thread
which reads from log_path from the file start, causing stale content to be
replayed when a user-provided log_fn already exists; before starting the tail
(after resolving log_path and creating parent dirs) ensure existing files are
truncated so only new solver output is streamed (e.g., if not cleanup and
log_path.exists(): open/truncate the file or unlink+recreate it), keeping the
temporary-file behavior unchanged when cleanup is True; update logic near
stream_solver_log/_tail startup where log_path is prepared and before
thread.start() to perform the truncation.
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In `@flixopt/optimization.py`:
- Around line 247-249: The code mutates the caller's solver by setting
solver.log_to_console = False inside the capture block; preserve and restore the
original solver.log_to_console value (e.g., save orig = solver.log_to_console
before changing, set solver.log_to_console = False for the duration of the with
fx_io.stream_solver_log(log_fn=log_fn) context, and restore
solver.log_to_console = orig in a finally or after the context) so the solver
object isn't left mutated after solve() returns; apply the same pattern to the
analogous mutation in flow_system.py where solver.log_to_console is changed.
- Explain the setting is independent of log_to_console
- Document the double-logging scenario clearly (capture + native console + console logger = double output)
- Provide three example configurations showing how to avoid it:
- File capture only: capture=True, log_to_console=False, enable_file()
- Logger to console: capture=True, log_to_console=False, enable_console()
- Native console only: capture=False, log_to_console=True
The user is in full control — if they enable both, they get both. The docs make it obvious how to avoid double logging.
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In `@flixopt/config.py`:
- Around line 744-748: The debug() and exploring() presets enable both a console
logger via Logging.enable_console(...) and direct solver console output via
Solving.log_to_console = True while also setting Solving.capture_solver_log =
True, causing duplicate console output; update the debug() and exploring()
functions to set Solving.log_to_console = False (matching notebook() and
production()) so solver output is only routed through the Python logger when
capture_solver_log is enabled, keep Solving.capture_solver_log = True and
preserve the call to Logging.enable_console(...) unchanged.
In `@flixopt/flow_system.py`:
- Around line 1442-1455: FlowSystem.solve currently differs from
Optimization.solve: when CONFIG.Solving.capture_solver_log is True it uses
fx_io.stream_solver_log() which creates a temporary (deleted) file, and when
False it omits log_fn entirely; make behavior consistent by always passing a
log_fn to self.model.solve (like Optimization.solve). Update FlowSystem.solve
signature to accept an optional log_file parameter (or derive a persistent path
if none provided), then: if CONFIG.Solving.capture_solver_log use
fx_io.stream_solver_log() to obtain a path and pass it as log_fn to
self.model.solve; otherwise ensure a persistent log path is obtained/created (or
use the provided log_file) and pass that as log_fn; alternatively, if this
difference is intentional, add a short comment in FlowSystem.solve referencing
Optimization.solve to document the divergence.
🧹 Nitpick comments (1)
tests/utilities/test_config.py (1)
167-167: Consider addingcapture_solver_logassertions to the other preset tests.Only
test_preset_exploringverifies the new flag. Thesilent,debug,production, andnotebookpresets also setcapture_solver_logbut lack corresponding assertions, which would catch regressions if preset logic changes.
…at already exists, it's emptied before the tail thread starts. The cleanup=True (temp file) path is unchanged since mkstemp already creates a fresh empty file.
…ts users persist the solver log to a specific file
…solver log itself
Summary
flixopt.solverchild logger viaCONFIG.Solving.capture_solver_loglog_fn), a background thread tails the file and forwards each line tologging.getLogger('flixopt.solver')at INFO levelMotivation
Previously, solver output was C-level stdout that bypassed Python's logging system entirely. This made it impossible to capture solver logs in file handlers, filter them independently, or integrate them with structured logging pipelines.
Usage
Changes
flixopt/io.py: Addedstream_solver_log()context manager (file tailing via background thread)flixopt/config.py: Addedcapture_solver_logsetting (defaultFalse), updated presetsflixopt/flow_system.py:FlowSystem.solve()uses log capture when enabledflixopt/optimization.py:Optimization.solve()uses log capture when enabledType of Change
Testing
Checklist
Summary by CodeRabbit
New Features
Configuration / Presets
Documentation
Tests