Skip to content

fix: replace print() with logger in responses, reloader, and __init__#1328

Open
mango766 wants to merge 4 commits intosparckles:mainfrom
mango766:fix/replace-print-with-logging
Open

fix: replace print() with logger in responses, reloader, and __init__#1328
mango766 wants to merge 4 commits intosparckles:mainfrom
mango766:fix/replace-print-with-logging

Conversation

@mango766
Copy link
Copy Markdown
Contributor

@mango766 mango766 commented Mar 12, 2026

Summary

  • Replace print() calls with proper logger usage in non-CLI modules
  • Fix broken format strings in reloader.py that printed tuples instead of formatted messages

Changes

File print() removed Details
responses.py 1 Async generator error now uses logger.error()
reloader.py 6 Rust compilation logging uses logger.info/error
__init__.py 1 Rust compilation message uses logger.info()

Bug Fix: Broken Format Strings

reloader.py had several broken format strings using print("msg: %s", arg) which doesn't do string interpolation — it just prints the tuple ("msg: %s", arg). These are now fixed with proper logger calls that correctly format the arguments.

Before:

print("Created rust file : %s", rust_file)
# Output: ('Created rust file : %s', 'foo.rs')

After:

logger.info("Created rust file: %s", rust_file)
# Output: Created rust file: foo.rs

Note

CLI-facing output in cli.py is intentionally left as print() since those are user-facing messages.

Test Plan

  • Verify Python syntax — passed
  • robyn --dev correctly shows reloader logs
  • Streaming responses correctly log errors

Summary by CodeRabbit

  • Refactor
    • Standardized error reporting by replacing direct console prints with structured logging across core runtime components, improving diagnostic output and consistency.
    • Added a default no-op log handler to prevent spurious "no handlers" warnings when no logging is configured, reducing noisy console output.

Replace print() calls with proper logging in non-CLI modules:

- responses.py: Error in async generator now uses logger.error()
  instead of silently printing to stdout
- reloader.py: 6 print() calls replaced with logger.info/error,
  also fixes broken format strings that used print("msg: %s", arg)
  which printed the tuple literal instead of formatting
- __init__.py: Rust compilation message uses logger.info()

CLI-facing output in cli.py is intentionally left as print() since
those are user-facing messages, not log entries.
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
robyn Ready Ready Preview, Comment Mar 13, 2026 1:58pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 12, 2026

📝 Walkthrough

Walkthrough

Module-level logging was introduced and direct prints replaced with logger calls across reloader and responses; Logger now installs a NullHandler to avoid missing-handler warnings. Control flow and public APIs were unchanged.

Changes

Cohort / File(s) Summary
Reloader logging
robyn/reloader.py
Replaced user-facing print calls with logger.info/logger.error in compile_rust_files, create_rust_file, clean_rust_binaries, and reload paths; no control-flow changes.
Responses error logging
robyn/responses.py
Added module-level logger and replaced print on async-generator exceptions with logger.exception in get_next() and AsyncGeneratorWrapper._get_next_value().
Logger default handler
robyn/logger.py
Added a NullHandler in Logger.__init__ to prevent "No handlers could be found" warnings when no logging handlers are configured.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested reviewers

  • sansyrox

Poem

🐰
I hopped through code with careful paws,
Swapping prints for logs without a pause.
Now errors whisper where they should,
Handlers quiet, everything good.
Hooray — the meadow's neat because! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: replacing print() calls with logger usage across the specified modules.
Description check ✅ Passed The description is comprehensive and well-structured, covering the motivation, detailed changes with examples, and test plan.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Copy link
Copy Markdown

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
robyn/reloader.py (1)

15-31: ⚠️ Potential issue | 🟠 Major

Logger messages will be silently dropped in dev mode.

When running robyn --dev app.py, the setup_reloader() function is called before any Robyn instance is created. Since logging.basicConfig() is only called in BaseRobyn.__init__, the logger has no handlers configured when setup_reloader(), compile_rust_files(), and other functions in this file execute. Python's default root logger level is WARNING, so INFO-level messages are silently dropped (logger.error() and logger.warning() calls will still appear via the lastResort handler).

This affects lines 20, 31, 87, 95, and 130 where logger.info() is called.

Add logging configuration at the start of setup_reloader():

🔧 Proposed fix
 def setup_reloader(directory_path: str, file_path: str) -> None:
+    import logging
+    logging.basicConfig(level=logging.INFO)
+
     event_handler = EventHandler(file_path, directory_path)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@robyn/reloader.py` around lines 15 - 31, The logger is used before BaseRobyn
sets up handlers, so INFO messages from setup_reloader(), compile_rust_files(),
and other functions are dropped; fix by ensuring logging is configured at the
start of setup_reloader() (e.g., call logging.basicConfig(...) or add a
StreamHandler with level INFO if no handlers exist) so logger.info(...) calls
produce output; update setup_reloader() to check logging.getLogger().handlers
and configure a default handler/level (INFO) when absent so calls in
compile_rust_files(), setup_reloader(), and related functions are visible in dev
mode.
🧹 Nitpick comments (1)
robyn/responses.py (1)

121-124: Consider using logger.exception() for full traceback.

The current implementation logs only the exception message. If debugging async generator issues is important, logger.exception() would automatically include the stack trace.

♻️ Optional improvement
         except Exception as e:
             # Log error and stop iteration
-            logger.error("Error in async generator: %s", e)
+            logger.exception("Error in async generator")
             raise StopIteration
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@robyn/responses.py` around lines 121 - 124, Replace the logger.error call in
the async generator exception handler with logger.exception so the full
traceback is recorded; in robyn/responses.py update the except Exception as e
block that currently calls logger.error("Error in async generator: %s", e) to
call logger.exception("Error in async generator") (or logger.exception with
exc_info=True) immediately before raising StopIteration so the stack trace for
the async generator (the exception handler around the generator function) is
preserved in logs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@robyn/__init__.py`:
- Around line 71-73: The module-level logger.info call after compile_rust_files
(when using compile_path := config.compile_rust_path) runs at import time before
BaseRobyn.__init__ configures logging, so INFO will be dropped; either move
logging.basicConfig() to module scope before the compile block (so
logger.info(...) is handled), or replace the logger.info("Compiled rust files")
call with a direct print("Compiled rust files") (or similar CLI-friendly output)
to guarantee the compilation status is visible; locate the
compile_rust_files/compile_path check and the logger.info call in
robyn/__init__.py and update accordingly (or instead relocate
BaseRobyn.__init__'s basicConfig invocation to module level if you choose that
approach).

In `@robyn/reloader.py`:
- Around line 128-130: The "Reloading the server" message in the reload method
may be dropped by unconfigured logging; update the reload function (method
reload) to emit CLI-facing output directly (use print("Reloading the server"))
instead of logger.info, or alternatively ensure the module-level logger (logger)
has a CLI handler before calling reload (e.g., configure a
StreamHandler/basicConfig during initialization where the server is started).
Make the change in the reload method (and ensure consistency with stop_server if
it also needs CLI-visible messages).

---

Outside diff comments:
In `@robyn/reloader.py`:
- Around line 15-31: The logger is used before BaseRobyn sets up handlers, so
INFO messages from setup_reloader(), compile_rust_files(), and other functions
are dropped; fix by ensuring logging is configured at the start of
setup_reloader() (e.g., call logging.basicConfig(...) or add a StreamHandler
with level INFO if no handlers exist) so logger.info(...) calls produce output;
update setup_reloader() to check logging.getLogger().handlers and configure a
default handler/level (INFO) when absent so calls in compile_rust_files(),
setup_reloader(), and related functions are visible in dev mode.

---

Nitpick comments:
In `@robyn/responses.py`:
- Around line 121-124: Replace the logger.error call in the async generator
exception handler with logger.exception so the full traceback is recorded; in
robyn/responses.py update the except Exception as e block that currently calls
logger.error("Error in async generator: %s", e) to call logger.exception("Error
in async generator") (or logger.exception with exc_info=True) immediately before
raising StopIteration so the stack trace for the async generator (the exception
handler around the generator function) is preserved in logs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4e4ec8bc-c119-4171-87a9-a00438155847

📥 Commits

Reviewing files that changed from the base of the PR and between 7aeaef4 and b8dc528.

📒 Files selected for processing (3)
  • robyn/__init__.py
  • robyn/reloader.py
  • robyn/responses.py

Comment on lines 128 to +130
def reload(self) -> None:
self.stop_server()
print("Reloading the server")
logger.info("Reloading the server")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Reload message may not appear without handler configuration.

The "Reloading the server" message is important user feedback during hot reload. With the logging handler issue, this message will be silently dropped. Given this is CLI-facing feedback, consider keeping this as print() or ensuring logging is configured as suggested above.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@robyn/reloader.py` around lines 128 - 130, The "Reloading the server" message
in the reload method may be dropped by unconfigured logging; update the reload
function (method reload) to emit CLI-facing output directly (use
print("Reloading the server")) instead of logger.info, or alternatively ensure
the module-level logger (logger) has a CLI handler before calling reload (e.g.,
configure a StreamHandler/basicConfig during initialization where the server is
started). Make the change in the reload method (and ensure consistency with
stop_server if it also needs CLI-visible messages).

Address review feedback: the compile_rust_files message at line 73 runs
at module import time, before BaseRobyn.__init__ configures logging.
Revert to print() for this specific CLI-facing message to ensure it is
always visible.
Add logging.basicConfig(level=logging.INFO) at module level in
reloader.py so that logger.info/error calls in compile_rust_files()
are not silently dropped when invoked before Robyn.__init__ configures
logging.

Also use logger.exception() instead of logger.error() in responses.py
to include traceback in async generator error handling.

Co-Authored-By: Claude (claude-opus-4-6) <noreply@anthropic.com>
Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@robyn/reloader.py`:
- Around line 15-18: The module-level call
logging.basicConfig(level=logging.INFO) in reloader.py overrides user log
configuration; remove that basicConfig call and instead attach a NullHandler to
this module's logger (e.g.,
getLogger(__name__).addHandler(logging.NullHandler())) so early import-time
loggers are silenced until Robyn.__init__ performs full configuration, or
alternatively implement the minimal change by setting force=True in
Robyn.__init__'s logging.basicConfig(level=self.config.log_level, force=True) to
allow reconfiguration—pick one approach and apply it consistently (modify
reloader.py to add a NullHandler if you choose the first approach, or update
Robyn.__init__ if you choose the force=True approach).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 68ce06c6-a805-4033-8dca-d7b7667d3ecd

📥 Commits

Reviewing files that changed from the base of the PR and between b8dc528 and 6661480.

📒 Files selected for processing (2)
  • robyn/reloader.py
  • robyn/responses.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • robyn/responses.py

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Mar 13, 2026

Merging this PR will not alter performance

✅ 183 untouched benchmarks


Comparing mango766:fix/replace-print-with-logging (2010cc6) with main (5384049)

Open in CodSpeed

…logging config

Co-Authored-By: Claude (claude-opus-4-6) <noreply@anthropic.com>
Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
robyn/logger.py (1)

22-22: NullHandler attachment is already idempotent in practice.

The Logger class is instantiated only once at module level (logger = Logger() at line 81), so handler accumulation is not a concern. The current implementation at line 22 is safe and does not need modification.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@robyn/logger.py` at line 22, The review points out that attaching a
NullHandler is idempotent and since the Logger class is instantiated once at
module level (the Logger class and the module-level instantiation logger =
Logger()), no handler accumulation occurs; therefore do not change or remove the
existing self.logger.addHandler(logging.NullHandler()) call in the Logger
class—leave it as-is to preserve safe default handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@robyn/logger.py`:
- Line 22: The review points out that attaching a NullHandler is idempotent and
since the Logger class is instantiated once at module level (the Logger class
and the module-level instantiation logger = Logger()), no handler accumulation
occurs; therefore do not change or remove the existing
self.logger.addHandler(logging.NullHandler()) call in the Logger class—leave it
as-is to preserve safe default handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f78d81f3-4241-4d93-9d94-361e0934d438

📥 Commits

Reviewing files that changed from the base of the PR and between 6661480 and 2010cc6.

📒 Files selected for processing (2)
  • robyn/logger.py
  • robyn/reloader.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • robyn/reloader.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant