Skip to content

Add SUPPRESS_ACCESS_LOGS setting and logging configuration tests#10

Merged
ochui merged 4 commits intomainfrom
feat/enhance-logging
Apr 5, 2026
Merged

Add SUPPRESS_ACCESS_LOGS setting and logging configuration tests#10
ochui merged 4 commits intomainfrom
feat/enhance-logging

Conversation

@ochui
Copy link
Copy Markdown
Member

@ochui ochui commented Apr 5, 2026

This pull request introduces support for suppressing Uvicorn access logs via a new SUPPRESS_ACCESS_LOGS configuration option, updates documentation to reflect this new setting, and adds related tests. Additionally, it adds Qlty code quality configuration files to the repository.

Logging and configuration enhancements:

  • Added a new SUPPRESS_ACCESS_LOGS environment variable and corresponding suppress_access_logs field to QueueServerSettings, allowing users to suppress Uvicorn access logs by default. The logging configuration in asgi.py was updated to respect this setting. [1] [2] [3] [4]
  • Updated documentation in README.md and docs/configuration.rst to describe the new SUPPRESS_ACCESS_LOGS option, including its default value and behavior. [1] [2] [3]

Testing improvements:

  • Added unit tests to verify the behavior of the suppress_access_logs setting in both the settings loader and the logging configuration. [1] [2]

Code quality tooling:

  • Added Qlty configuration files (.qlty/.gitignore, .qlty/configs/.hadolint.yaml, .qlty/qlty.toml) to enable and configure code quality checks for the repository. [1] [2] [3]

Summary by CodeRabbit

  • New Features

    • New SUPPRESS_ACCESS_LOGS environment variable (default: true) to suppress server access logs and centralized logging configuration to honor it.
  • Documentation

    • Added SUPPRESS_ACCESS_LOGS to configuration docs and README.
  • Tests

    • Added tests covering logging configuration and access-log suppression behavior.
  • Chores

    • Added project quality configs and updated ignore rules for tooling files.

ochui added 3 commits April 5, 2026 22:55
- Introduced `SUPPRESS_ACCESS_LOGS` setting to control Uvicorn access logs.
- Updated `configure_logging` function to accept the new setting.
- Enhanced `QueueServerSettings` to include the new configuration option.
- Added unit tests for logging configuration behavior based on the new setting.
Copilot AI review requested due to automatic review settings April 5, 2026 22:16
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 5, 2026

📝 Walkthrough

Walkthrough

The PR adds a SUPPRESS_ACCESS_LOGS setting (default true) to settings and uses a new fq_server.logging.configure_logging to optionally disable Uvicorn access logs; it updates docs, tests, and adds Qlty tooling configs and a .codex gitignore entry.

Changes

Cohort / File(s) Summary
Gitignore & Qlty tooling
\.gitignore, .qlty/.gitignore, .qlty/configs/.hadolint.yaml, .qlty/qlty.toml
Added .codex to root .gitignore. Added .qlty allowlist-style .gitignore, Hadolint config ignoring DL3008, and a full qlty.toml enabling lint/security plugins and exclude/test patterns.
Settings & ASGI logging integration
asgi.py, fq_server/settings.py, fq_server/logging.py
Added suppress_access_logs: bool to QueueServerSettings (env SUPPRESS_ACCESS_LOGS, default True), moved logging setup into new fq_server.logging.configure_logging(log_level, suppress_access_logs), and updated ASGI entrypoint to call it.
Documentation
README.md, docs/configuration.rst
Documented new SUPPRESS_ACCESS_LOGS env var and default value in README and configuration docs.
Tests
tests/test_config_settings.py, tests/test_logging_config.py
Added tests verifying default/override behavior for suppress_access_logs and a new test suite asserting configure_logging sets uvicorn.access.disabled and fq_server logger level appropriately.

Sequence Diagram(s)

sequenceDiagram
    participant ASGI as "ASGI entrypoint\n(asgi.py)"
    participant Settings as "QueueServerSettings\n(fq_server/settings.py)"
    participant LoggerUtil as "fq_server.logging\n(configure_logging)"
    participant Uvicorn as "uvicorn.access\n(logger)"

    ASGI->>Settings: load settings (including SUPPRESS_ACCESS_LOGS)
    ASGI->>LoggerUtil: configure_logging(log_level, suppress_access_logs)
    LoggerUtil->>LoggerUtil: resolve log level, maybe basicConfig
    LoggerUtil->>Uvicorn: set disabled = suppress_access_logs
    LoggerUtil->>ASGI: return (logging configured)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 Soft paws on keys, I tweak the logs tonight,
Quiet the access hum, put the noise to flight.
Qlty tends the burrow, docs polished with care,
Tests snug like carrots — all tidy and fair. 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary changes: adding the SUPPRESS_ACCESS_LOGS setting and corresponding logging configuration tests.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/enhance-logging

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.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 5, 2026

Codecov Report

❌ Patch coverage is 80.00000% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
fq_server/logging.py 75.00% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new SUPPRESS_ACCESS_LOGS environment setting to disable Uvicorn access logs by default, wires it into the ASGI entrypoint’s logging setup, documents the new option, and introduces tests and Qlty configuration files.

Changes:

  • Introduce SUPPRESS_ACCESS_LOGS (suppress_access_logs) in settings and apply it in asgi.py logging configuration.
  • Add unit tests covering settings parsing and access-log suppression behavior.
  • Add Qlty code quality configuration files and update ignore patterns.

Reviewed changes

Copilot reviewed 9 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
asgi.py Extends configure_logging to optionally disable uvicorn.access and wires it to settings at import time.
fq_server/settings.py Adds suppress_access_logs setting with env alias and boolean parsing validator.
tests/test_config_settings.py Adds coverage for default/override behavior of SUPPRESS_ACCESS_LOGS.
tests/test_logging_config.py Adds tests for configure_logging toggling uvicorn.access.disabled.
README.md Documents SUPPRESS_ACCESS_LOGS in the env var table.
docs/configuration.rst Documents SUPPRESS_ACCESS_LOGS and its default.
.qlty/qlty.toml Adds Qlty configuration (generated by qlty init).
.qlty/configs/.hadolint.yaml Adds hadolint ignore configuration.
.qlty/.gitignore Scopes what’s tracked inside .qlty/.
.gitignore Adds .codex to ignored files.
.codex Adds an empty .codex file (currently tracked).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +3 to +8
import logging
import unittest

from asgi import configure_logging


Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

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

Importing configure_logging from asgi triggers asgi.py module import side effects (QueueServerSettings.from_env(), setup_server(...), and app construction). That makes this unit test heavier than needed and can introduce coupling to external dependencies (e.g., Flowdacity Queue / Redis behavior) unrelated to logging configuration. Consider moving configure_logging into a side-effect-free module (e.g., fq_server/logging.py) and importing it from both asgi.py and this test, or otherwise isolating app/server initialization from imports used by unit tests.

Suggested change
import logging
import unittest
from asgi import configure_logging
import ast
import logging
import unittest
from pathlib import Path
def _load_configure_logging():
asgi_path = Path(__file__).resolve().parent.parent / "asgi.py"
module_ast = ast.parse(asgi_path.read_text(encoding="utf-8"), filename=str(asgi_path))
for node in module_ast.body:
if isinstance(node, ast.FunctionDef) and node.name == "configure_logging":
isolated_module = ast.Module(body=[node], type_ignores=[])
ast.fix_missing_locations(isolated_module)
namespace = {"logging": logging}
exec(compile(isolated_module, filename=str(asgi_path), mode="exec"), namespace)
return namespace["configure_logging"]
raise AssertionError("configure_logging function not found in asgi.py")
configure_logging = _load_configure_logging()

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +15
self.original_disabled = self.access_logger.disabled

def tearDown(self):
self.access_logger.disabled = self.original_disabled
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

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

configure_logging() can call logging.basicConfig(...) when the root logger has no handlers, which mutates global logging state for the entire test process. This test currently restores only uvicorn.access.disabled in tearDown, so the root logger handler/level changes may leak into other tests. Consider snapshotting and restoring the root logger’s handlers/level (and any logger state you change) in setUp/tearDown to keep tests isolated.

Suggested change
self.original_disabled = self.access_logger.disabled
def tearDown(self):
self.access_logger.disabled = self.original_disabled
self.original_disabled = self.access_logger.disabled
self.root_logger = logging.getLogger()
self.original_root_handlers = self.root_logger.handlers[:]
self.original_root_level = self.root_logger.level
def tearDown(self):
self.access_logger.disabled = self.original_disabled
self.root_logger.handlers[:] = self.original_root_handlers
self.root_logger.setLevel(self.original_root_level)

Copilot uses AI. Check for mistakes.
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 (2)
tests/test_logging_config.py (1)

6-6: Consider isolating configure_logging from ASGI module side effects for unit tests.

Line 6 imports from asgi, which also initializes app startup objects at module import time. Extracting logging config into a side-effect-free module would keep this test focused and reduce incidental coupling.

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

In `@tests/test_logging_config.py` at line 6, The test imports configure_logging
from the asgi module which triggers app startup side-effects; extract the
logging setup into a side-effect-free module (e.g., a new module like
logging_config with a single configure_logging function) and update tests to
import configure_logging from that module instead of from asgi; ensure asgi
imports the new logging_config.configure_logging where needed so runtime
behavior is unchanged while unit tests can import the pure function without
initializing app startup objects.
tests/test_config_settings.py (1)

69-76: Consider adding one invalid-value test for SUPPRESS_ACCESS_LOGS.

A small follow-up could assert rejection of values like "yes"/"1" to fully cover the validator path for this new field.

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

In `@tests/test_config_settings.py` around lines 69 - 76, Add a test that asserts
invalid values for SUPPRESS_ACCESS_LOGS are rejected by the validator: create a
new test (e.g., test_queue_server_settings_suppress_access_logs_invalid_value)
that calls QueueServerSettings.from_env({"SUPPRESS_ACCESS_LOGS": "yes"}) and/or
{"SUPPRESS_ACCESS_LOGS": "1"} and verifies it raises the expected
ValidationError (or appropriate exception) rather than returning a boolean for
suppress_access_logs; reference the existing tests
test_queue_server_settings_suppress_access_logs_default and
test_queue_server_settings_suppress_access_logs_override to mirror setup and
assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/test_config_settings.py`:
- Around line 69-76: Add a test that asserts invalid values for
SUPPRESS_ACCESS_LOGS are rejected by the validator: create a new test (e.g.,
test_queue_server_settings_suppress_access_logs_invalid_value) that calls
QueueServerSettings.from_env({"SUPPRESS_ACCESS_LOGS": "yes"}) and/or
{"SUPPRESS_ACCESS_LOGS": "1"} and verifies it raises the expected
ValidationError (or appropriate exception) rather than returning a boolean for
suppress_access_logs; reference the existing tests
test_queue_server_settings_suppress_access_logs_default and
test_queue_server_settings_suppress_access_logs_override to mirror setup and
assertions.

In `@tests/test_logging_config.py`:
- Line 6: The test imports configure_logging from the asgi module which triggers
app startup side-effects; extract the logging setup into a side-effect-free
module (e.g., a new module like logging_config with a single configure_logging
function) and update tests to import configure_logging from that module instead
of from asgi; ensure asgi imports the new logging_config.configure_logging where
needed so runtime behavior is unchanged while unit tests can import the pure
function without initializing app startup objects.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 020885cb-0719-4924-a838-60db3ceb591a

📥 Commits

Reviewing files that changed from the base of the PR and between c7f880a and 6579801.

📒 Files selected for processing (11)
  • .codex
  • .gitignore
  • .qlty/.gitignore
  • .qlty/configs/.hadolint.yaml
  • .qlty/qlty.toml
  • README.md
  • asgi.py
  • docs/configuration.rst
  • fq_server/settings.py
  • tests/test_config_settings.py
  • tests/test_logging_config.py

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 (2)
fq_server/logging.py (1)

6-10: Consider using the LogLevelName type for stronger type safety.

The function accepts str but only works correctly with the five standard log level names. The codebase already defines LogLevelName = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] in fq_server/settings.py. Using this type would provide better IDE support and catch invalid values at type-check time.

💡 Proposed type hint improvement
 # Copyright (c) 2025 Flowdacity Development Team. See LICENSE.txt for details.
 
 import logging
+from typing import Literal
+
+LogLevelName = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
 
 
 def configure_logging(
-    log_level: str,
+    log_level: LogLevelName,
     suppress_access_logs: bool = True,
 ) -> None:

Alternatively, import the type from settings:

from fq_server.settings import LogLevelName
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fq_server/logging.py` around lines 6 - 10, Change the configure_logging
signature to accept the stronger type LogLevelName instead of str: import
LogLevelName from fq_server.settings and update the parameter annotation in
configure_logging to use that type (keeping suppress_access_logs and the body
unchanged), so IDEs and type-checkers will enforce only the five allowed level
names when calling configure_logging; ensure you update any callers if needed to
satisfy the new type.
tests/test_logging_config.py (1)

29-42: Tests cover the core functionality well.

The three test cases validate:

  1. Access logger suppression when enabled
  2. Access logger re-enabled when suppression is disabled
  3. Logger level propagation to fq_server logger

Consider adding a test for the basicConfig conditional behavior (root logger configuration when no handlers exist) for more complete coverage.

💡 Optional: Additional test for basicConfig behavior
def test_configure_logging_adds_handler_when_none_exist(self):
    # Remove all root handlers to simulate fresh state
    for handler in list(self.root_logger.handlers):
        self.root_logger.removeHandler(handler)
    
    configure_logging("DEBUG", suppress_access_logs=False)
    
    self.assertGreater(len(self.root_logger.handlers), 0)
    self.assertEqual(self.root_logger.level, logging.DEBUG)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_logging_config.py` around lines 29 - 42, Add a test that verifies
configure_logging invokes logging.basicConfig when there are no root handlers:
remove all handlers from the root logger (use the existing self.root_logger),
call configure_logging("DEBUG", suppress_access_logs=False), then assert that
self.root_logger.handlers has length > 0 and that self.root_logger.level equals
logging.DEBUG; reference the existing test helpers/fixtures (self.root_logger,
configure_logging, fq_logger, access_logger) to keep consistent setup/teardown.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@fq_server/logging.py`:
- Around line 6-10: Change the configure_logging signature to accept the
stronger type LogLevelName instead of str: import LogLevelName from
fq_server.settings and update the parameter annotation in configure_logging to
use that type (keeping suppress_access_logs and the body unchanged), so IDEs and
type-checkers will enforce only the five allowed level names when calling
configure_logging; ensure you update any callers if needed to satisfy the new
type.

In `@tests/test_logging_config.py`:
- Around line 29-42: Add a test that verifies configure_logging invokes
logging.basicConfig when there are no root handlers: remove all handlers from
the root logger (use the existing self.root_logger), call
configure_logging("DEBUG", suppress_access_logs=False), then assert that
self.root_logger.handlers has length > 0 and that self.root_logger.level equals
logging.DEBUG; reference the existing test helpers/fixtures (self.root_logger,
configure_logging, fq_logger, access_logger) to keep consistent setup/teardown.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6842570e-005b-41e9-bdd8-631bb039f536

📥 Commits

Reviewing files that changed from the base of the PR and between 6579801 and fed1168.

📒 Files selected for processing (3)
  • asgi.py
  • fq_server/logging.py
  • tests/test_logging_config.py

@ochui ochui merged commit fed1168 into main Apr 5, 2026
6 of 7 checks passed
@ochui ochui deleted the feat/enhance-logging branch April 5, 2026 22:31
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.

2 participants