renamed python-logging

This commit is contained in:
John Lancaster
2026-07-02 23:04:54 -05:00
parent eeeb6ecdbe
commit d4ca78dbfb
6 changed files with 158 additions and 125 deletions
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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.
@@ -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`.
+140
View File
@@ -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`.
@@ -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.