Skip to content

Conversation

@michrzan
Copy link

@michrzan michrzan commented Dec 11, 2025

Add --quiet-logs option to minimize console output

Summary

This PR adds a --quiet-logs flag to edr monitor, edr report, and edr send-report commands that suppresses INFO-level logs on the console, showing only WARNING and above.

Motivation

When running Elementary in CI/CD pipelines or automated environments, the verbose INFO logs can clutter output and make it harder to spot actual issues. This option allows users to run with minimal console noise while still capturing everything in the log file.

Usage

edr monitor --quiet-logs true [other options...]
edr report --quiet-logs true [other options...]

Behavior

  • Default (--quiet-logs false): Shows INFO and above (unchanged behavior)
  • When enabled: Only WARNING, ERROR, and CRITICAL messages shown on console
  • Log file: All messages still written to {target-path}/edr.log
  • Debug mode (EDR_DEBUG=1): Takes precedence over --quiet-logs
  • Final status messages ("Update skipped alerts", "Update sent alerts") always display regardless of the setting.

Changes

  • elementary/cli/cli.py: Parse --quiet-logs early and pass to logger setup
  • elementary/monitor/cli.py: Add --quiet-logs option to common options
  • elementary/utils/log.py: Support quiet_logs parameter in handler setup
  • elementary/monitor/fetchers/alerts/alerts.py: Use click.echo() for status messages

Summary by CodeRabbit

  • New Features

    • Added a --quiet-logs CLI option to suppress informational startup text and show only warnings/errors for cleaner automated output.
    • Logging now respects --quiet-logs, reducing console verbosity when enabled.
  • User-facing changes

    • Some status messages are now printed via standard CLI output to improve readability in interactive runs.

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions
Copy link
Contributor

👋 @michrzan
Thank you for raising your pull request.
Please make sure to add tests and document all user-facing changes.
You can do this by editing the docs files in this pull request.

@michrzan michrzan temporarily deployed to elementary_test_env December 11, 2025 08:34 — with GitHub Actions Inactive
@coderabbitai
Copy link

coderabbitai bot commented Dec 11, 2025

📝 Walkthrough

Walkthrough

Adds a new CLI flag --quiet-logs, a helper to detect it, and threads it through monitor/report/send_report to conditionally configure console logging level and suppress some CLI/welcome output; alert fetchers now use click.echo for status messages.

Changes

Cohort / File(s) Summary
CLI core / flag parsing
elementary/cli/cli.py
Adds get_quiet_logs(ctx) to detect --quiet-logs from CLI context and uses its result to conditionally suppress welcome text and an upfront info log.
Monitor CLI surface
elementary/monitor/cli.py
Registers --quiet-logs via common_options; adds quiet_logs parameter to monitor, report, and send_report and forwards it into Config/initialization flows.
Logging utilities
elementary/utils/log.py
Adds get_console_handler(quiet_logs: bool = False) and updates set_root_logger_handlers(..., quiet_logs: bool = False) to select handler level: DEBUG (debug mode) / WARNING (quiet_logs) / INFO (default); root logger set to DEBUG and propagation disabled.
Alert fetchers CLI output
elementary/monitor/fetchers/alerts/alerts.py
Replaces logger.info(...) calls with click.echo(...) in skip_alerts() and update_sent_alerts() for status messages.
Config surface
elementary/config/config.py
Adds optional quiet_logs: Optional[bool] = None parameter to Config.__init__ signature; value is accepted and propagated but not otherwise used in shown diff.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant User
participant CLI as CLI (click)
participant Config as Config ctor
participant LogUtils as set_root_logger_handlers
participant Console as Console Handler

User->>CLI: invoke command with/without --quiet-logs
CLI->>CLI: get_quiet_logs(ctx)
CLI->>Config: construct Config(..., quiet_logs=bool)
CLI->>LogUtils: set_root_logger_handlers(name, path, quiet_logs=bool)
LogUtils->>Console: get_console_handler(quiet_logs)
alt quiet_logs == true
    Console-->>LogUtils: level = WARNING
else debug mode
    Console-->>LogUtils: level = DEBUG
else
    Console-->>LogUtils: level = INFO
end
LogUtils-->>CLI: handlers attached
CLI->>User: welcome/info suppressed if quiet_logs else shown
Note over Console,CLI: Alerts output uses click.echo instead of logger.info

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A hush hops in on silent paws,
Flags tucked away to calm the cause.
Echoes chirp where logs once sang,
Warning ears now lightly hang.
Quiet hops—soft, small applause.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.43% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: quiet-logs flag' clearly summarizes the main change: adding a --quiet-logs flag to suppress INFO-level console logs in EDR CLI commands.
✨ Finishing touches
  • 📝 Generate docstrings

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

@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 (5)
elementary/utils/log.py (1)

63-73: Root logger handler setup is consistent; consider guarding against duplicate handlers

Using logger.propagate = False, a DEBUG logger level, and a quiet_logs-aware console handler is a solid setup that isolates the “elementary” logger tree and keeps files at full verbosity.

If set_root_logger_handlers can be called more than once in a process (e.g., tests or re‑entry), consider clearing or reusing existing handlers to avoid accumulating multiple console/file handlers:

 def set_root_logger_handlers(logger_name, files_target_path, quiet_logs: bool = False):
     logger = logging.getLogger(logger_name)

     # Disable propagation to root logger to avoid duplicate logs
     logger.propagate = False
+
+    # Optional: reset handlers to avoid duplicates on repeated setup
+    logger.handlers.clear()
elementary/monitor/fetchers/alerts/alerts.py (1)

4-5: Status messages via click.echo meet “always visible” requirement

Switching from logger.info to click.echo for “Update skipped alerts/sent alerts” ensures these final status messages are printed even when --quiet-logs suppresses INFO logs, which matches the PR goal. If AlertsFetcher is ever reused outside CLI contexts, you may later want to inject an output callable instead of hard‑wiring click.echo, but for current CLI usage this is fine.

Also applies to: 34-35, 63-66

elementary/cli/cli.py (1)

32-38: Align get_quiet_logs parsing with Click’s boolean semantics

Current parsing (ctx.args.index("--quiet-logs") and value.lower() == "true") works for the documented --quiet-logs true/false usage, but it can drift from Click’s own type=bool behavior (e.g., values like 1, yes, or --quiet-logs=true, and env‑var defaults via EDR_QUIET_LOGS won’t be honored here).

Consider delegating parsing to Click’s BOOL type to keep semantics in sync:

 def get_quiet_logs(ctx):
     try:
         ctx_args = ctx.args
         idx = ctx_args.index("--quiet-logs")
-        return ctx_args[idx + 1].lower() == "true"
+        raw_value = ctx_args[idx + 1]
+        # Reuse Click's bool conversion so CLI arg and option parsing stay aligned.
+        return click.BOOL.convert(raw_value, param=None, ctx=ctx)
     except (ValueError, IndexError):
         return False

Optionally, you could also fall back to os.getenv("EDR_QUIET_LOGS") here if you want quiet mode configurable purely via env vars in CI.

elementary/monitor/cli.py (2)

152-157: --quiet-logs option wiring is fine; consider is_flag for a more idiomatic UX

The new --quiet-logs option is correctly added to common_options and matches the documented --quiet-logs true usage. If you ever want a more typical flag UX (--quiet-logs / --no-quiet-logs without explicit true/false), you could switch to an is_flag style declaration:

-        func = click.option(
-            "--quiet-logs",
-            type=bool,
-            default=False,
-            help="Minimize INFO level logs. Only WARNING and above will be shown.",
-        )(func)
+        func = click.option(
+            "--quiet-logs/--no-quiet-logs",
+            default=False,
+            help="Minimize INFO level logs. Only WARNING and above will be shown.",
+        )(func)

This would also simplify get_quiet_logs since you’d only need to detect presence of the flag, not parse string values.


333-334: Silence Ruff ARG001 by explicitly consuming quiet_logs parameters

quiet_logs is required in the signatures for monitor, report, and send_report so Click can pass the option, but it’s currently unused in the bodies, which triggers the Ruff ARG001 warnings.

If you don’t need per‑command behavior yet, a lightweight way to keep the parameter and quiet the linter is to explicitly consume it at the top of each function:

 def monitor(
     ctx,
@@
-    maximum_columns_in_alert_samples,
-    quiet_logs,
+    maximum_columns_in_alert_samples,
+    quiet_logs,
 ):
     """
     Get alerts on failures in dbt jobs.
     """
+    # Currently only used by the CLI entrypoint for logger setup.
+    _ = quiet_logs
@@
 def report(
@@
-    target_path,
-    quiet_logs,
+    target_path,
+    quiet_logs,
 ):
@@
+    _ = quiet_logs
@@
 def send_report(
@@
-    target_path,
-    quiet_logs,
+    target_path,
+    quiet_logs,
 ):
@@
+    _ = quiet_logs

This keeps the CLI surface stable while eliminating the unused‑argument warnings until you decide to use quiet_logs inside these commands.

Also applies to: 461-462, 692-693

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 272a40c and 0ddb210.

📒 Files selected for processing (4)
  • elementary/cli/cli.py (2 hunks)
  • elementary/monitor/cli.py (4 hunks)
  • elementary/monitor/fetchers/alerts/alerts.py (3 hunks)
  • elementary/utils/log.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
elementary/cli/cli.py (2)
elementary/utils/log.py (1)
  • set_root_logger_handlers (63-73)
elementary/utils/package.py (1)
  • get_package_version (9-10)
🪛 Ruff (0.14.8)
elementary/monitor/cli.py

333-333: Unused function argument: quiet_logs

(ARG001)


461-461: Unused function argument: quiet_logs

(ARG001)


692-692: Unused function argument: quiet_logs

(ARG001)

🔇 Additional comments (2)
elementary/utils/log.py (1)

33-41: Quiet log level selection matches intended behavior

get_console_handler correctly makes is_debug() take precedence over quiet_logs, and maps quiet mode to WARNING while leaving the default at INFO. This aligns with “EDR_DEBUG overrides quiet mode” and “suppress INFO on console only”.

elementary/cli/cli.py (1)

69-76: Logger initialization with quiet_logs is correctly ordered

Initializing handlers once in ElementaryCLI.invoke using get_log_path(ctx) and get_quiet_logs(ctx), then gating the welcome echo and version INFO log on not quiet_logs, cleanly implements the quiet‑mode behavior without affecting file logging.

@NoyaOffer
Copy link
Contributor

Thanks @michrzan ! I know this can help many users. We'll look into it!

Copy link
Contributor

@arbiv arbiv left a comment

Choose a reason for hiding this comment

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

Thanks @michrzan!
My only comment is that I would change the flag to be is_flag so users only need to set it, without setting boolean value:

func = click.option(
    "--quiet-logs",
    is_flag=True,
    default=False,
    help="Minimize INFO level logs. Only WARNING and above will be shown.",
)(func)

@michrzan michrzan requested a deployment to elementary_test_env December 31, 2025 13:50 — with GitHub Actions Waiting
@michrzan
Copy link
Author

michrzan commented Dec 31, 2025

Sounds good @arbiv!

Initially I noticed that other boolean parameters (e.g., --update-dbt-package, --exclude-elementary-models) were not flags so I thought to keep it this way for consistency. But I see now that there is already one option (--override-dbt-project-config) that is a flag and was introduced more recently. Let's make it a flag then.

I also update the Config to inlcude quiet_logs for consistency and to stop pyright from complaining about an unused variable.

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

Caution

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

⚠️ Outside diff range comments (1)
elementary/monitor/cli.py (1)

333-368: Remove quiet_logs parameter from Config constructor or store it as an instance variable.

The quiet_logs parameter is accepted by the Config class (line 78 in elementary/config/config.py) but is never stored as an instance variable, making it a dead parameter. While quiet_logs is correctly used for logging configuration in elementary/cli/cli.py before Config instantiation, once passed to the Config constructor at line 367 of elementary/monitor/cli.py, it is discarded and inaccessible to any downstream code. Either remove the parameter from the Config signature or store it as self.quiet_logs if it's needed for future use.

🧹 Nitpick comments (1)
elementary/cli/cli.py (1)

32-37: Minor: Exception handling can be simplified.

The in operator on a list only raises AttributeError if ctx.args is None or not iterable, not ValueError. While catching both is defensive, ValueError is unlikely in this context.

🔎 Simplified exception handling
 def get_quiet_logs(ctx):
     try:
         return "--quiet-logs" in ctx.args
-    except (ValueError, AttributeError):
+    except AttributeError:
         return False
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0ddb210 and 719dd75.

📒 Files selected for processing (3)
  • elementary/cli/cli.py
  • elementary/config/config.py
  • elementary/monitor/cli.py
🧰 Additional context used
🧬 Code graph analysis (1)
elementary/cli/cli.py (2)
elementary/utils/log.py (1)
  • set_root_logger_handlers (63-73)
elementary/utils/package.py (1)
  • get_package_version (9-10)
🪛 Ruff (0.14.10)
elementary/config/config.py

78-78: Unused method argument: quiet_logs

(ARG002)

🔇 Additional comments (5)
elementary/cli/cli.py (2)

69-74: LGTM! Clean conditional suppression of console output.

The implementation correctly suppresses the welcome message and version info log when quiet_logs is enabled, while preserving them for normal operation. This aligns with the PR objective to reduce console noise in CI/CD environments.


67-74: Implementation is sound; early flag detection via ctx.args works correctly.

The get_quiet_logs() function correctly detects the --quiet-logs flag by checking its presence in Click's raw argument list. Since the flag is defined with is_flag=True at the subcommand level and the check occurs at the MultiCommand.invoke() level before parameter processing, the flag will reliably appear in ctx.args whenever users specify it (e.g., edr monitor --quiet-logs). This approach is platform-independent and the try/except block properly handles edge cases.

elementary/monitor/cli.py (3)

152-157: LGTM! Well-defined CLI flag with clear help text.

The --quiet-logs flag is properly defined with is_flag=True, sensible defaults, and descriptive help text. The placement in common_options ensures it's available across all relevant commands (monitor, report, send-report).


462-477: Consistent parameter threading in the report command.

The quiet_logs parameter is properly added to the report command signature and passed to the Config constructor, maintaining consistency with the monitor command.


694-738: Consistent parameter threading in the send_report command.

The quiet_logs parameter is properly added to the send_report command signature and passed to the Config constructor, maintaining consistency with the other commands.

env: str = DEFAULT_ENV,
run_dbt_deps_if_needed: Optional[bool] = None,
project_name: Optional[str] = None,
quiet_logs: Optional[bool] = None,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: The quiet_logs parameter is not stored in the Config instance.

The parameter is accepted but never assigned to self.quiet_logs or used elsewhere in the constructor. This means the quiet_logs preference cannot be accessed by any code that uses the Config object, breaking the intended functionality.

🔎 Proposed fix to store the parameter

Add the assignment in the constructor body, similar to other parameters:

         self.env = env
         self.project_name = project_name
+        self.quiet_logs = quiet_logs

Consider adding this around line 87, near other simple assignments.

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Ruff (0.14.10)

78-78: Unused method argument: quiet_logs

(ARG002)

🤖 Prompt for AI Agents
In elementary/config/config.py around line 78, the constructor accepts the
quiet_logs parameter but never stores it on the Config instance; add an
assignment like self.quiet_logs = quiet_logs in the constructor body (near other
simple assignments around line ~87) so the value is accessible via the Config
object and preserves the Optional[bool] type.

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.

3 participants