From d4ca78dbfbb260088d7094567e55f35d3033ec1a Mon Sep 17 00:00:00 2001 From: John Lancaster <32917998+jsl12@users.noreply.github.com> Date: Thu, 2 Jul 2026 23:04:54 -0500 Subject: [PATCH] renamed python-logging --- docs/architecture.md | 2 +- docs/mcp_layout.md | 2 +- .../skills/python-logging-dictconfig/SKILL.md | 115 -------------- docs/skills/python-logging/SKILL.md | 140 ++++++++++++++++++ .../references/python-logging-docs.md | 14 +- zensical.toml | 10 +- 6 files changed, 158 insertions(+), 125 deletions(-) delete mode 100644 docs/skills/python-logging-dictconfig/SKILL.md create mode 100644 docs/skills/python-logging/SKILL.md rename docs/skills/{python-logging-dictconfig => python-logging}/references/python-logging-docs.md (53%) diff --git a/docs/architecture.md b/docs/architecture.md index 74f7f33..d36f96a 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -253,7 +253,7 @@ Allowed exception: Existing markdown reference sets are valid examples of authored source material for this architecture: 1. docs/skills/pytesting/references/pytest-docs.md -2. docs/skills/python-logging-dictconfig/references/python-logging-docs.md +2. docs/skills/python-logging/references/python-logging-docs.md 3. docs/skills/fastapi-uv-docker/references/fastapi-best-practices.md These inputs are treated as content sources, while resource URIs and catalog payloads remain the machine-facing contracts. diff --git a/docs/mcp_layout.md b/docs/mcp_layout.md index 8958372..caca976 100644 --- a/docs/mcp_layout.md +++ b/docs/mcp_layout.md @@ -194,7 +194,7 @@ This keeps docs publication explicit and predictable. Existing reference docs remain valid content inputs in this pattern: 1. docs/skills/pytesting/references/pytest-docs.md -2. docs/skills/python-logging-dictconfig/references/python-logging-docs.md +2. docs/skills/python-logging/references/python-logging-docs.md 3. docs/skills/fastapi-uv-docker/references/fastapi-best-practices.md These are source documents, not deployment artifacts. diff --git a/docs/skills/python-logging-dictconfig/SKILL.md b/docs/skills/python-logging-dictconfig/SKILL.md deleted file mode 100644 index 88e3239..0000000 --- a/docs/skills/python-logging-dictconfig/SKILL.md +++ /dev/null @@ -1,115 +0,0 @@ ---- -name: python-logging-dictconfig -description: 'Set up idiomatic Python logging with logging.config.dictConfig. Use when creating or refactoring logging setup, standardizing handlers/formatters, and enforcing centralized config.' -x-personal-mcp: - id: python-logging-dictconfig - version: 1.0.0 - tags: - - logging - - python - - observability - capabilities: - - resource://skills/python-logging-dictconfig/document ---- - -# Idiomatic Python Logging with dictConfig - -Use this skill to produce a minimal, centralized logging setup using `logging.config.dictConfig`. - -Load references only when needed: -- Python logging overview and hierarchy: [Python logging references](./references/python-logging-docs.md) - -## When to Use - -- A project configures logging ad hoc with `basicConfig` across multiple modules. -- You need one canonical logging configuration for app startup. -- You need consistent formatting and levels across console/file handlers. -- You want library modules to use named loggers without configuring logging themselves. - -## Inputs To Collect - -1. Runtime type: script, library, web app, worker, CLI. -2. Destinations: stdout only, file only, or both. -3. Desired default level: `INFO`, `DEBUG`, etc. -4. Whether third-party loggers should be tuned (for example `uvicorn`, `sqlalchemy`). - -If missing, assume: -- stdout handler -- human-readable formatter -- root level `INFO` -- `disable_existing_loggers: False` - -## Procedure - -1. Define a single `LOGGING` dictionary in one startup-oriented module (for example `logging_config.py`). -2. Include `version: 1` and set `disable_existing_loggers: False` unless there is a specific reason to silence existing loggers. -3. Define formatters first, then handlers, then logger routing (`root` and optional named `loggers`). -4. Use `logging.config.dictConfig(LOGGING)` exactly once during application startup. -5. In all modules, get loggers via `logger = logging.getLogger(__name__)` and never call `basicConfig`. -6. Keep libraries configuration-free: libraries should emit logs, applications decide routing. -7. Verify behavior with a quick smoke check at multiple levels (`DEBUG`, `INFO`, `WARNING`, `ERROR`). - -## Minimal Baseline Templates - -### Configuration - -!!! warning "Don't use the name `logging.py` because it will conflict - -```python title="logging_config.py" -import logging.config - -LOGGING = { - "version": 1, - "disable_existing_loggers": False, - "formatters": { - "basic": { - "format": "%(asctime)s.%(msecs)03d [%(levelname)s] %(message)s", - "datefmt": "%Y-%m-%d %H:%M:%S", - } - }, - "handlers": { - "console": { - "class": "logging.StreamHandler", - "formatter": "basic", - "stream": "ext://sys.stdout", - } - }, - "root": { - "level": "INFO", - "handlers": ["console"], - }, -} - -def configure_logging() -> None: - logging.config.dictConfig(LOGGING) -``` - -```python title="app.py" -# app startup -from .logging_config import configure_logging - -configure_logging() -``` - -### Usage - -The preferred way of instantiating loggers is at the top of modules like this: - -```python -import logging - -logger = logging.getLogger(__name__) -``` - -## Completion Checks -1. `dictConfig` is called once at startup, not per module. -2. No `basicConfig` calls remain. -3. Modules use `getLogger(__name__)`. -4. Logs appear at expected level and destination. -5. Third-party logger noise is intentionally configured or left at defaults. -6. No module named `logging.py` in the project. - -## Branching Guidance -- If structured logs are required: switch formatter output to JSON while keeping `dictConfig` topology unchanged. -- If both console and file output are needed: add a file handler and attach it to `root`. -- If a specific framework logger is too noisy: add a named logger override under `loggers`. diff --git a/docs/skills/python-logging/SKILL.md b/docs/skills/python-logging/SKILL.md new file mode 100644 index 0000000..1b4f7e0 --- /dev/null +++ b/docs/skills/python-logging/SKILL.md @@ -0,0 +1,140 @@ +--- +name: python-logging +description: 'Design, review, or refactor Python logging. Use when choosing logger names, levels, handlers, library/application boundaries, basicConfig, dictConfig, structured logs, or operational logging defaults.' +x-personal-mcp: + id: python-logging + version: 1.0.0 + tags: + - logging + - python + - observability + capabilities: + - resource://skills/python-logging/document +--- + +# Python Logging + +Use this skill to produce idiomatic Python logging guidance or a small logging setup for an application, library, CLI, worker, or web service. + +Load references only when needed: +- Python logging overview, library guidance, handlers, and dictConfig schema: [Python logging references](./references/python-logging-docs.md) + +## When to Use + +- A project mixes `print`, root logger calls, scattered `basicConfig`, or ad hoc handlers. +- You need to choose logging levels, destinations, formatter fields, or logger names. +- You need a clear boundary between library logging and application logging configuration. +- You need a centralized logging setup, including a `logging.config.dictConfig` section. +- You are tuning framework or third-party loggers such as `uvicorn`, `sqlalchemy`, or HTTP clients. + +## Inputs To Collect + +1. Runtime type: script, library, CLI, web app, worker, service, or notebook. +2. Audience: humans in a terminal, operators in files, machines in JSON, or test assertions. +3. Destinations: stdout/stderr, file, rotating file, queue, syslog, external collector, or none for libraries. +4. Default level and verbosity controls: `INFO`, `DEBUG`, CLI flag, environment variable, or config file. +5. Operational constraints: async event loop, multiprocessing, container logs, sensitive data, or high-volume paths. + +If missing, assume: +- application code, not a reusable library +- stdout console logging +- human-readable formatter +- root level `INFO` +- no file logging unless requested + +## Procedure + +1. Classify the project boundary first: application code configures logging; library code emits logs and avoids configuring handlers. +2. In modules, create loggers with `logger = logging.getLogger(__name__)` so logger names follow the package hierarchy. +3. Use level semantics consistently: `DEBUG` for diagnosis, `INFO` for normal milestones, `WARNING` for notable recoverable conditions, `ERROR` for failed operations, and `CRITICAL` for process-threatening failures. +4. Prefer parameterized logging calls such as `logger.info("Processed %s items", count)` so message formatting is deferred until the record is emitted. +5. Configure handlers and formatters once during application startup. For small scripts, `basicConfig` can be enough; for applications, prefer a centralized configuration function. +6. Keep third-party logger overrides explicit and narrow. Tune noisy loggers by name instead of muting broad logger hierarchies. +7. Smoke-check output at expected levels and destinations, including one suppressed `DEBUG` message and one exception path if errors are logged. + +## Best Practices + +- Do not name a module `logging.py`; it shadows the standard library package. +- Do not call `basicConfig` or attach handlers in every module. +- Do not log to the root logger from libraries. Use named loggers and, only if needed, attach `logging.NullHandler()` to the library's top-level logger. +- Do not create loggers per request, user, file, or connection. Use contextual fields, adapters, or filters instead. +- Use `logger.exception(...)` only inside an exception handler when the traceback is useful. +- For async or high-throughput code, avoid slow network or file handlers on the hot path; consider `QueueHandler` and a listener. +- Avoid custom levels unless there is a strong interoperability reason. + +## Using dictConfig + +Use `logging.config.dictConfig` when configuration should be centralized, data-driven, or richer than `basicConfig`. + +1. Define one `LOGGING` dictionary in a startup-oriented module such as `logging_config.py`. +2. Include `version: 1` and usually set `disable_existing_loggers: False` so existing named loggers are not silently disabled. +3. Define formatters, then handlers, then logger routing with `root` and optional named `loggers`. +4. Call `logging.config.dictConfig(LOGGING)` once during application startup. +5. Keep application logging calls unchanged when adding new destinations or formats. + +### Minimal dictConfig Baseline + +```python title="logging_config.py" +import logging.config + +LOGGING = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "console": { + "format": "%(asctime)s.%(msecs)03d %(levelname)s %(name)s %(message)s", + "datefmt": "%Y-%m-%d %H:%M:%S", + } + }, + "handlers": { + "console": { + "class": "logging.StreamHandler", + "formatter": "console", + "stream": "ext://sys.stdout", + } + }, + "root": { + "level": "INFO", + "handlers": ["console"], + }, +} + + +def configure_logging() -> None: + logging.config.dictConfig(LOGGING) +``` + +```python title="app.py" +from .logging_config import configure_logging + +configure_logging() +``` + +```python title="feature.py" +import logging + +logger = logging.getLogger(__name__) + + +def run(count: int) -> None: + logger.info("Processing %s items", count) +``` + +## Branching Guidance + +- If the code is a tiny script: use `basicConfig` once near the entry point and module loggers elsewhere. +- If the code is a library: remove handlers and configuration calls; document logger names and optionally add `NullHandler` at the package root. +- If structured logs are required: keep the same logger and handler topology, but switch formatter output to JSON or a structured formatter. +- If console and file output are needed: add one file or rotating-file handler and attach it centrally. +- If multiple processes write to one file: use a queue/listener or process-safe collection path rather than opening the same file independently in each process. +- If a framework logger is noisy: add a named logger override with a level and leave unrelated logger propagation alone. + +## Completion Checks + +1. Modules use `logging.getLogger(__name__)`. +2. Application startup configures logging once. +3. Libraries do not configure application handlers. +4. Levels match the severity semantics in this skill. +5. Logs include enough context to identify source, severity, and event without leaking secrets. +6. Expected destinations receive messages and suppressed levels stay quiet. +7. No source file or package is named `logging.py`. diff --git a/docs/skills/python-logging-dictconfig/references/python-logging-docs.md b/docs/skills/python-logging/references/python-logging-docs.md similarity index 53% rename from docs/skills/python-logging-dictconfig/references/python-logging-docs.md rename to docs/skills/python-logging/references/python-logging-docs.md index 91d0510..2603516 100644 --- a/docs/skills/python-logging-dictconfig/references/python-logging-docs.md +++ b/docs/skills/python-logging/references/python-logging-docs.md @@ -1,6 +1,6 @@ -# Python Logging References +# Python Logging Source References -Use these official Python docs when applying this skill. +Use these official Python docs when applying the Python logging skill. ## Core Documentation @@ -10,13 +10,15 @@ Use these official Python docs when applying this skill. - [logging API reference](https://docs.python.org/3/library/logging.html) - [logging.config reference](https://docs.python.org/3/library/logging.config.html) -## dictConfig-Specific +## Configuration And dictConfig !!! info "dictConfig references" - [Dictionary schema details](https://docs.python.org/3/library/logging.config.html#logging-config-dictschema) for `version`, formatters, handlers, loggers, and root. - [`logging.config.dictConfig`](https://docs.python.org/3/library/logging.config.html#logging.config.dictConfig) function reference. ## Practical Notes -- Prefer app-level centralized config with one startup call to `dictConfig`. -- In modules, use `logging.getLogger(__name__)`. -- Avoid calling `basicConfig` in libraries or scattered modules. +- Prefer module loggers created with `logging.getLogger(__name__)`. +- Let applications configure handlers and formatters; libraries should emit logs without taking over routing. +- Use `basicConfig` for simple scripts and `dictConfig` for centralized application configuration. +- Explicitly set `disable_existing_loggers: False` in `dictConfig` unless disabling existing non-root loggers is intentional. +- Use queue-based handlers when slow handlers would block async, threaded, or high-volume code paths. diff --git a/zensical.toml b/zensical.toml index 2c2332c..2f0be87 100644 --- a/zensical.toml +++ b/zensical.toml @@ -94,6 +94,7 @@ nav = [ { "Engine" = "skills/fastapi-async-sqlalchemy-modernization/references/engine.md" }, { "Session" = "skills/fastapi-async-sqlalchemy-modernization/references/session.md" }, { "Tx" = "skills/fastapi-async-sqlalchemy-modernization/references/transactions.md" }, + { "SQLModel" = "skills/fastapi-async-sqlalchemy-modernization/references/sqlmodel.md" }, { "IO" = "skills/fastapi-async-sqlalchemy-modernization/references/implicit_io.md" }, { "Obs" = "skills/fastapi-async-sqlalchemy-modernization/references/observability.md" }, { "Template" = "skills/fastapi-async-sqlalchemy-modernization/references/template.md" }, @@ -121,8 +122,13 @@ nav = [ { "Ecosystem" = "skills/mcp-details/references/ecosystem-and-tooling.md" }, ] }, { "Logging" = [ - { "Overview" = "skills/python-logging-dictconfig/SKILL.md" }, - { "Docs" = "skills/python-logging-dictconfig/references/python-logging-docs.md" }, + { "Overview" = "skills/python-logging/SKILL.md" }, + { "Docs" = "skills/python-logging/references/python-logging-docs.md" }, + ] }, + { "Pydantic Settings" = [ + { "Overview" = "skills/pydantic-settings/SKILL.md" }, + { "Source Docs" = "skills/pydantic-settings/references/source-documentation.md" }, + { "Workflow" = "skills/pydantic-settings/references/implementation-workflow.md" }, ] }, { "Ruff" = [ { "Overview" = "skills/ruff-linting-formating/SKILL.md" },