logging
This commit is contained in:
@@ -12,12 +12,14 @@ Load references only when needed:
|
|||||||
- Python logging overview and hierarchy: [Python logging references](./references/python-logging-docs.md)
|
- Python logging overview and hierarchy: [Python logging references](./references/python-logging-docs.md)
|
||||||
|
|
||||||
## When to Use
|
## When to Use
|
||||||
|
|
||||||
- A project configures logging ad hoc with `basicConfig` across multiple modules.
|
- A project configures logging ad hoc with `basicConfig` across multiple modules.
|
||||||
- You need one canonical logging configuration for app startup.
|
- You need one canonical logging configuration for app startup.
|
||||||
- You need consistent formatting and levels across console/file handlers.
|
- You need consistent formatting and levels across console/file handlers.
|
||||||
- You want library modules to use named loggers without configuring logging themselves.
|
- You want library modules to use named loggers without configuring logging themselves.
|
||||||
|
|
||||||
## Inputs To Collect
|
## Inputs To Collect
|
||||||
|
|
||||||
1. Runtime type: script, library, web app, worker, CLI.
|
1. Runtime type: script, library, web app, worker, CLI.
|
||||||
2. Destinations: stdout only, file only, or both.
|
2. Destinations: stdout only, file only, or both.
|
||||||
3. Desired default level: `INFO`, `DEBUG`, etc.
|
3. Desired default level: `INFO`, `DEBUG`, etc.
|
||||||
@@ -30,6 +32,7 @@ If missing, assume:
|
|||||||
- `disable_existing_loggers: False`
|
- `disable_existing_loggers: False`
|
||||||
|
|
||||||
## Procedure
|
## Procedure
|
||||||
|
|
||||||
1. Define a single `LOGGING` dictionary in one startup-oriented module (for example `logging_config.py`).
|
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.
|
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`).
|
3. Define formatters first, then handlers, then logger routing (`root` and optional named `loggers`).
|
||||||
@@ -38,24 +41,28 @@ If missing, assume:
|
|||||||
6. Keep libraries configuration-free: libraries should emit logs, applications decide routing.
|
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`).
|
7. Verify behavior with a quick smoke check at multiple levels (`DEBUG`, `INFO`, `WARNING`, `ERROR`).
|
||||||
|
|
||||||
## Minimal Baseline Template
|
## Minimal Baseline Templates
|
||||||
```python
|
|
||||||
# logging_config.py
|
### Configuration
|
||||||
from logging.config import dictConfig
|
|
||||||
|
!!! warning "Don't use the name `logging.py` because it will conflict
|
||||||
|
|
||||||
|
```python title="logging_config.py"
|
||||||
|
import logging.config
|
||||||
|
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"disable_existing_loggers": False,
|
"disable_existing_loggers": False,
|
||||||
"formatters": {
|
"formatters": {
|
||||||
"standard": {
|
"basic": {
|
||||||
"format": "%(asctime)s %(levelname)s %(name)s: %(message)s"
|
"format": "%(asctime)s.%(msecs)03d [%(levelname)s] %(message)s",
|
||||||
|
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"handlers": {
|
"handlers": {
|
||||||
"console": {
|
"console": {
|
||||||
"class": "logging.StreamHandler",
|
"class": "logging.StreamHandler",
|
||||||
"level": "INFO",
|
"formatter": "basic",
|
||||||
"formatter": "standard",
|
|
||||||
"stream": "ext://sys.stdout",
|
"stream": "ext://sys.stdout",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -66,22 +73,24 @@ LOGGING = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure_logging() -> None:
|
def configure_logging() -> None:
|
||||||
dictConfig(LOGGING)
|
logging.config.dictConfig(LOGGING)
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
```python title="app.py"
|
||||||
# app startup
|
# app startup
|
||||||
from .logging_config import configure_logging
|
from .logging_config import configure_logging
|
||||||
|
|
||||||
configure_logging()
|
configure_logging()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
The preferred way of instantiating loggers is at the top of modules like this:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# any module
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
logger.info("module initialized")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Completion Checks
|
## Completion Checks
|
||||||
@@ -90,6 +99,7 @@ logger.info("module initialized")
|
|||||||
3. Modules use `getLogger(__name__)`.
|
3. Modules use `getLogger(__name__)`.
|
||||||
4. Logs appear at expected level and destination.
|
4. Logs appear at expected level and destination.
|
||||||
5. Third-party logger noise is intentionally configured or left at defaults.
|
5. Third-party logger noise is intentionally configured or left at defaults.
|
||||||
|
6. No module named `logging.py` in the project.
|
||||||
|
|
||||||
## Branching Guidance
|
## Branching Guidance
|
||||||
- If structured logs are required: switch formatter output to JSON while keeping `dictConfig` topology unchanged.
|
- If structured logs are required: switch formatter output to JSON while keeping `dictConfig` topology unchanged.
|
||||||
|
|||||||
Reference in New Issue
Block a user