Skip to content

Establish enforced logging port (#286) - #292

Draft
leynos wants to merge 1 commit into
mainfrom
issue-286-establish-a-centrally-managed-logging-port-with-architecture-enforcement-against-direct-logger-invocation
Draft

Establish enforced logging port (#286)#292
leynos wants to merge 1 commit into
mainfrom
issue-286-establish-a-centrally-managed-logging-port-with-architecture-enforcement-against-direct-logger-invocation

Conversation

@leynos

@leynos leynos commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #286.

  • Establishes episodic.logging as the explicit logging port, with an opaque
    LoggerHandle and wrappers for debug, info, warning, error, and exception
    emission.
  • Migrates production logging calls through the port, including structured
    orchestration, observability, and provider-error paths.
  • Enforces the femtologging import boundary with Hecate and direct-call
    regressions through ty and a production-tree scanner.
  • Documents the port contract, correlation-context boundary, and enforcement
    rules for future cross-cutting logging changes.

Validation

  • make check-fmt
  • make lint
  • make typecheck
  • make check-architecture
  • make markdownlint
  • make nixie
  • Focused logging, architecture, observability, orchestration, Skylos, and
    timeout-retry tests.
  • make test completed with 1,230 passing and 3 skipped; its two
    timeout-only environment-wiring tests then passed in isolated sequential
    retries.

References

Summary by Sourcery

Enforce episodic.logging as the sole application logging port and prevent production code from bypassing it.

New Features:

  • Establish an opaque LoggerHandle and explicit wrapper functions as the public episodic.logging port for debug, info, warning, error, and exception emission.

Bug Fixes:

  • Route production logging and structured observability events through the logging port while preserving exception and structured-event behavior.

Enhancements:

  • Replace direct logger access across orchestration, provider-error, worker, ingestion, and observability paths with the common logging contract.

Documentation:

  • Document the logging-port contract, backend import boundary, correlation-context boundary, and enforcement rules for future logging changes.
  • Document Hecate external-package classification and the logging architecture groups.

Tests:

  • Add type-checking, production-tree scanning, and architecture-fixture coverage to prevent direct logger calls and imports of femtologging outside the logging port.
  • Update logging, observability, orchestration, worker, and executor tests for opaque handles and structured port emissions.

Wrap femtologging behind an opaque LoggerHandle and route all Episodic
emission paths through level-specific port helpers. This makes correlation
context attachable at one seam and rejects raw logger method calls.

Enforce the backend import boundary with Hecate, add type and scanner
regressions, and document the logging-port contract.
@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Warning

Your free Security trial is over. An organization admin can activate billing to continue.


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

@sourcery-ai

sourcery-ai Bot commented Aug 25, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR establishes episodic.logging as the sole production logging seam by wrapping femtologging behind an opaque LoggerHandle, migrating operational and structured logging paths to wrapper-based emission, and enforcing both import and direct-call boundaries through Hecate, ty, and a production-tree regression scanner.

File-Level Changes

Change Details Files
Introduces an opaque application logging port that centralizes formatting and backend dispatch.
  • Adds LoggerHandle and debug/info/warning/error/exception wrapper functions.
  • Preserves convenience-method and low-level log() backend compatibility.
  • Makes exception logging include traceback information by default and retains configuration and constructor aliases.
episodic/logging.py
tests/test_logging.py
tests/__snapshots__/test_logging.ambr
Migrates production call sites from direct logger methods and stdlib logging to the new port.
  • Updates authorization, canonical, worker, generation, LLM provider, orchestration, QA, concurrency, and migration paths.
  • Converts structured metadata and extra fields into JSON payloads emitted through the wrappers.
  • Updates observability adapters and test spies to inject LoggerHandle instances.
episodic/api/authorization.py
episodic/canonical/ingestion_service.py
episodic/canonical/services.py
episodic/canonical/storage/migration_check.py
episodic/canonical/storage/uow.py
episodic/concurrent_interpreters.py
episodic/generation/chapter_marker_generator.py
episodic/llm/openai_api/utils.py
episodic/observability.py
episodic/orchestration/_types.py
episodic/qa/chrono.py
episodic/qa/chrono_langgraph.py
episodic/worker/runtime.py
tests/test_chrono.py
tests/test_chrono_langgraph.py
tests/test_interpreter_executor_observability.py
tests/test_observability.py
tests/test_worker_routing_contract.py
tests/fixtures/llm.py
Adds layered enforcement preventing production code from bypassing the logging port.
  • Defines Hecate logging-port, logging-backend, and external-library groups with import-order-sensitive permissions.
  • Adds a typecheck fixture proving LoggerHandle level-method access is rejected.
  • Scans the production tree for direct logger method calls and adds Hecate fixture coverage for forbidden femtologging imports.
pyproject.toml
tests/test_logging_port_enforcement.py
tests/architecture_hecate_config.py
tests/test_architecture_enforcement.py
tests/test_architecture_hecate_config.py
tests/features/architecture_enforcement.feature
tests/steps/test_architecture_enforcement_steps.py
tests/fixtures/architecture/femtologging_outside_logging_port/__init__.py
tests/fixtures/architecture/femtologging_outside_logging_port/api.py
tests/fixtures/architecture/femtologging_outside_logging_port/logging.py
Documents the logging contract and architecture rules for future changes.
  • Describes wrapper usage, percent-style formatting, opaque-handle semantics, correlation-context boundaries, and enforcement gates.
  • Documents Hecate external-package classification and logging group ordering requirements.
docs/developers-guide.md

Assessment against linked issues

Issue Objective Addressed Explanation
#286 Establish and document episodic.logging as the sole public logging port, including debug and exception wrappers, an opaque logger handle, and a contract for future cross-cutting context such as correlation identifiers.
#286 Enforce the import boundary so only episodic.logging can import the raw femtologging backend, with architecture fixtures proving that direct backend imports fail.
#286 Prevent and eliminate direct logger-method invocations throughout production code, using the opaque handle, type-checking and regression checks, and migrate existing logging call sites through the port.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Gates Failed
Enforce advisory code health rules (3 files with Code Duplication, Large Method)

Our agent can fix these. Install it.

Gates Passed
5 Quality Gates Passed

Reason for failure
Enforce advisory code health rules Violations Code Health Impact
test_chrono_langgraph.py 1 advisory rule 10.00 → 9.39 Suppress
architecture_hecate_config.py 1 advisory rule 10.00 → 9.60 Suppress
logging.py 1 advisory rule 9.39 → 9.10 Suppress

See analysis details in CodeScene

Quality Gate Profile: Pay Down Tech Debt
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.

Comment thread episodic/logging.py


def log_error(
logger: _CompatibleLogger,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

❌ Getting worse: Code Duplication
introduced similar code in: log_debug,log_exception

Suppress

Comment on lines +270 to +348
def _external_logging_fixture_config(package: str) -> str:
"""Return a fixture policy that enforces the femtologging boundary."""
return textwrap.dedent(
f"""\
[tool.hecate]
root_packages = ["{package}"]
default_rule_id = "ARCH001"
include_external_packages = true

[[tool.hecate.groups]]
name = "logging_port"
prefixes = ["{package}.logging"]
allowed = ["logging_port", "logging_backend"]

[[tool.hecate.groups]]
name = "logging_backend"
prefixes = ["femtologging"]
allowed = ["logging_backend"]

[[tool.hecate.groups]]
name = "external_libraries"
prefixes = [
"alembic",
"celery",
"falcon",
"httpx",
"langgraph",
"openai",
"pydantic",
"sqlalchemy",
]
allowed = ["external_libraries"]

[[tool.hecate.groups]]
name = "composition_root"
prefixes = ["{package}.runtime"]
allowed = [
"application",
"composition_root",
"domain",
"external_libraries",
"inbound_adapter",
"logging_port",
"outbound_adapter",
]

[[tool.hecate.groups]]
name = "domain"
prefixes = ["{package}.domain"]
allowed = ["domain", "external_libraries", "logging_port"]

[[tool.hecate.groups]]
name = "application"
prefixes = ["{package}.service"]
allowed = ["application", "domain", "external_libraries", "logging_port"]

[[tool.hecate.groups]]
name = "inbound_adapter"
prefixes = ["{package}.api"]
allowed = [
"application",
"domain",
"external_libraries",
"inbound_adapter",
"logging_port",
]

[[tool.hecate.groups]]
name = "outbound_adapter"
prefixes = ["{package}.storage"]
allowed = [
"application",
"domain",
"external_libraries",
"logging_port",
"outbound_adapter",
]
"""
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

❌ New issue: Large Method
_external_logging_fixture_config has 71 lines, threshold = 70

Suppress

errors: list[tuple[str, tuple[object, ...], dict[str, object]]] = []

def capture_error(
logger: object,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

❌ New issue: Code Duplication
The module contains 2 functions with similar structure: test_chrono_node_logs_evaluation_failure,test_chrono_node_logs_missing_request

Suppress

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No quality gates enabled for this code.

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.

Establish a centrally managed logging port with architecture enforcement against direct logger invocation

1 participant