stogger 2026.5.12__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- stogger-2026.5.12/.agents/drafts/pytest-stogger-new-checks.md +284 -0
- stogger-2026.5.12/.agents/impl_specs/logging-decorators-docs.md +91 -0
- stogger-2026.5.12/.agents/impl_specs/logging-decorators.md +322 -0
- stogger-2026.5.12/.agents/impl_specs/postgres-target.md +255 -0
- stogger-2026.5.12/.agents/impl_specs/pytest-stogger-new-checks.md +187 -0
- stogger-2026.5.12/.agents/impl_specs/raw-output.md +110 -0
- stogger-2026.5.12/.agents/reports/quality-audit.md +371 -0
- stogger-2026.5.12/.envrc +2 -0
- stogger-2026.5.12/.gitignore +34 -0
- stogger-2026.5.12/.pre-commit-config.yaml +26 -0
- stogger-2026.5.12/.python-version +1 -0
- stogger-2026.5.12/.vulture_whitelist.py +168 -0
- stogger-2026.5.12/AGENTS.md +20 -0
- stogger-2026.5.12/CONVENTIONS.md +428 -0
- stogger-2026.5.12/LICENSE +235 -0
- stogger-2026.5.12/PKG-INFO +12 -0
- stogger-2026.5.12/README.md +66 -0
- stogger-2026.5.12/conftest.py +25 -0
- stogger-2026.5.12/coverage.json +1 -0
- stogger-2026.5.12/docs/.gitignore +15 -0
- stogger-2026.5.12/docs/Makefile +28 -0
- stogger-2026.5.12/docs/_build/html/.buildinfo +4 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/__intersphinx_cache__/python_objects.inv +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/__intersphinx_cache__/structlog_objects.inv +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/api/index.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/api/stogger/config/index.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/api/stogger/core/index.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/api/stogger/factory/index.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/api/stogger/index.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/api/stogger/processors/index.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/dev/adr/format-config-extension.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/dev/adr/index.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/dev/adr/postgres-target.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/dev/adr/stogger-self-logging.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/dev/adr/stogger-systemd.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/dev/index.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/dev/testing_guide.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/dev/type_checking_guide.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/environment.pickle +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/index.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/user/cheatsheet.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/user/getting_started.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/user/index.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/user/logging_patterns.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/user/nix_integration.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/user/postgres.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/user/systemd.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/.doctrees/user/testing.doctree +0 -0
- stogger-2026.5.12/docs/_build/html/404.html +324 -0
- stogger-2026.5.12/docs/_build/html/_images/stogger_logo_ascii.txt +14 -0
- stogger-2026.5.12/docs/_build/html/_modules/index.html +327 -0
- stogger-2026.5.12/docs/_build/html/_modules/stogger/config.html +1001 -0
- stogger-2026.5.12/docs/_build/html/_modules/stogger/core.html +1442 -0
- stogger-2026.5.12/docs/_build/html/_modules/stogger/factory.html +566 -0
- stogger-2026.5.12/docs/_build/html/_modules/stogger/processors.html +355 -0
- stogger-2026.5.12/docs/_build/html/_sources/api/index.rst.txt +7 -0
- stogger-2026.5.12/docs/_build/html/_sources/api/stogger/config/index.rst.txt +320 -0
- stogger-2026.5.12/docs/_build/html/_sources/api/stogger/core/index.rst.txt +414 -0
- stogger-2026.5.12/docs/_build/html/_sources/api/stogger/factory/index.rst.txt +49 -0
- stogger-2026.5.12/docs/_build/html/_sources/api/stogger/index.rst.txt +27 -0
- stogger-2026.5.12/docs/_build/html/_sources/api/stogger/processors/index.rst.txt +37 -0
- stogger-2026.5.12/docs/_build/html/_sources/dev/adr/format-config-extension.md.txt +159 -0
- stogger-2026.5.12/docs/_build/html/_sources/dev/adr/index.md.txt +10 -0
- stogger-2026.5.12/docs/_build/html/_sources/dev/adr/postgres-target.md.txt +208 -0
- stogger-2026.5.12/docs/_build/html/_sources/dev/adr/stogger-self-logging.md.txt +125 -0
- stogger-2026.5.12/docs/_build/html/_sources/dev/adr/stogger-systemd.md.txt +203 -0
- stogger-2026.5.12/docs/_build/html/_sources/dev/index.md.txt +8 -0
- stogger-2026.5.12/docs/_build/html/_sources/dev/testing_guide.md.txt +174 -0
- stogger-2026.5.12/docs/_build/html/_sources/dev/type_checking_guide.md.txt +147 -0
- stogger-2026.5.12/docs/_build/html/_sources/index.md.txt +71 -0
- stogger-2026.5.12/docs/_build/html/_sources/user/cheatsheet.md.txt +115 -0
- stogger-2026.5.12/docs/_build/html/_sources/user/getting_started.md.txt +93 -0
- stogger-2026.5.12/docs/_build/html/_sources/user/index.md.txt +13 -0
- stogger-2026.5.12/docs/_build/html/_sources/user/logging_patterns.md.txt +429 -0
- stogger-2026.5.12/docs/_build/html/_sources/user/nix_integration.md.txt +79 -0
- stogger-2026.5.12/docs/_build/html/_sources/user/postgres.md.txt +187 -0
- stogger-2026.5.12/docs/_build/html/_sources/user/systemd.md.txt +180 -0
- stogger-2026.5.12/docs/_build/html/_sources/user/testing.md.txt +156 -0
- stogger-2026.5.12/docs/_build/html/_sphinx_design_static/design-tabs.js +101 -0
- stogger-2026.5.12/docs/_build/html/_sphinx_design_static/sphinx-design.min.css +1 -0
- stogger-2026.5.12/docs/_build/html/_static/base-stemmer.js +476 -0
- stogger-2026.5.12/docs/_build/html/_static/basic.css +906 -0
- stogger-2026.5.12/docs/_build/html/_static/check-solid.svg +4 -0
- stogger-2026.5.12/docs/_build/html/_static/clipboard.min.js +7 -0
- stogger-2026.5.12/docs/_build/html/_static/copy-button.svg +5 -0
- stogger-2026.5.12/docs/_build/html/_static/copybutton.css +94 -0
- stogger-2026.5.12/docs/_build/html/_static/copybutton.js +248 -0
- stogger-2026.5.12/docs/_build/html/_static/copybutton_funcs.js +73 -0
- stogger-2026.5.12/docs/_build/html/_static/debug.css +69 -0
- stogger-2026.5.12/docs/_build/html/_static/design-tabs.js +101 -0
- stogger-2026.5.12/docs/_build/html/_static/doctools.js +150 -0
- stogger-2026.5.12/docs/_build/html/_static/documentation_options.js +13 -0
- stogger-2026.5.12/docs/_build/html/_static/english-stemmer.js +1066 -0
- stogger-2026.5.12/docs/_build/html/_static/file.png +0 -0
- stogger-2026.5.12/docs/_build/html/_static/graphviz.css +12 -0
- stogger-2026.5.12/docs/_build/html/_static/language_data.js +13 -0
- stogger-2026.5.12/docs/_build/html/_static/minus.png +0 -0
- stogger-2026.5.12/docs/_build/html/_static/plus.png +0 -0
- stogger-2026.5.12/docs/_build/html/_static/pygments.css +250 -0
- stogger-2026.5.12/docs/_build/html/_static/scripts/furo-extensions.js +0 -0
- stogger-2026.5.12/docs/_build/html/_static/scripts/furo.js +3 -0
- stogger-2026.5.12/docs/_build/html/_static/scripts/furo.js.LICENSE.txt +7 -0
- stogger-2026.5.12/docs/_build/html/_static/scripts/furo.js.map +1 -0
- stogger-2026.5.12/docs/_build/html/_static/searchtools.js +693 -0
- stogger-2026.5.12/docs/_build/html/_static/skeleton.css +296 -0
- stogger-2026.5.12/docs/_build/html/_static/sphinx-design.min.css +1 -0
- stogger-2026.5.12/docs/_build/html/_static/sphinx_highlight.js +159 -0
- stogger-2026.5.12/docs/_build/html/_static/styles/furo-extensions.css +2 -0
- stogger-2026.5.12/docs/_build/html/_static/styles/furo-extensions.css.map +1 -0
- stogger-2026.5.12/docs/_build/html/_static/styles/furo.css +2 -0
- stogger-2026.5.12/docs/_build/html/_static/styles/furo.css.map +1 -0
- stogger-2026.5.12/docs/_build/html/_static/togglebutton.css +166 -0
- stogger-2026.5.12/docs/_build/html/_static/togglebutton.js +257 -0
- stogger-2026.5.12/docs/_build/html/api/index.html +368 -0
- stogger-2026.5.12/docs/_build/html/api/stogger/config/index.html +770 -0
- stogger-2026.5.12/docs/_build/html/api/stogger/core/index.html +1022 -0
- stogger-2026.5.12/docs/_build/html/api/stogger/factory/index.html +471 -0
- stogger-2026.5.12/docs/_build/html/api/stogger/index.html +394 -0
- stogger-2026.5.12/docs/_build/html/api/stogger/processors/index.html +421 -0
- stogger-2026.5.12/docs/_build/html/dev/adr/format-config-extension.html +640 -0
- stogger-2026.5.12/docs/_build/html/dev/adr/index.html +368 -0
- stogger-2026.5.12/docs/_build/html/dev/adr/postgres-target.html +691 -0
- stogger-2026.5.12/docs/_build/html/dev/adr/stogger-self-logging.html +548 -0
- stogger-2026.5.12/docs/_build/html/dev/adr/stogger-systemd.html +666 -0
- stogger-2026.5.12/docs/_build/html/dev/index.html +367 -0
- stogger-2026.5.12/docs/_build/html/dev/testing_guide.html +583 -0
- stogger-2026.5.12/docs/_build/html/dev/type_checking_guide.html +531 -0
- stogger-2026.5.12/docs/_build/html/genindex.html +746 -0
- stogger-2026.5.12/docs/_build/html/index.html +456 -0
- stogger-2026.5.12/docs/_build/html/llms-full.txt +3088 -0
- stogger-2026.5.12/docs/_build/html/llms.txt +29 -0
- stogger-2026.5.12/docs/_build/html/objects.inv +0 -0
- stogger-2026.5.12/docs/_build/html/py-modindex.html +365 -0
- stogger-2026.5.12/docs/_build/html/search.html +337 -0
- stogger-2026.5.12/docs/_build/html/searchindex.js +1 -0
- stogger-2026.5.12/docs/_build/html/user/cheatsheet.html +533 -0
- stogger-2026.5.12/docs/_build/html/user/getting_started.html +479 -0
- stogger-2026.5.12/docs/_build/html/user/index.html +371 -0
- stogger-2026.5.12/docs/_build/html/user/logging_patterns.html +893 -0
- stogger-2026.5.12/docs/_build/html/user/nix_integration.html +473 -0
- stogger-2026.5.12/docs/_build/html/user/postgres.html +627 -0
- stogger-2026.5.12/docs/_build/html/user/systemd.html +610 -0
- stogger-2026.5.12/docs/_build/html/user/testing.html +542 -0
- stogger-2026.5.12/docs/api/index.rst +7 -0
- stogger-2026.5.12/docs/api/stogger/config/index.rst +320 -0
- stogger-2026.5.12/docs/api/stogger/core/index.rst +414 -0
- stogger-2026.5.12/docs/api/stogger/factory/index.rst +49 -0
- stogger-2026.5.12/docs/api/stogger/index.rst +27 -0
- stogger-2026.5.12/docs/api/stogger/processors/index.rst +37 -0
- stogger-2026.5.12/docs/assets/stogger_logo_ascii.txt +14 -0
- stogger-2026.5.12/docs/conf.py +176 -0
- stogger-2026.5.12/docs/dev/adr/format-config-extension.md +159 -0
- stogger-2026.5.12/docs/dev/adr/index.md +10 -0
- stogger-2026.5.12/docs/dev/adr/postgres-target.md +208 -0
- stogger-2026.5.12/docs/dev/adr/stogger-self-logging.md +125 -0
- stogger-2026.5.12/docs/dev/adr/stogger-systemd.md +203 -0
- stogger-2026.5.12/docs/dev/index.md +8 -0
- stogger-2026.5.12/docs/dev/testing_guide.md +174 -0
- stogger-2026.5.12/docs/dev/type_checking_guide.md +147 -0
- stogger-2026.5.12/docs/index.md +71 -0
- stogger-2026.5.12/docs/user/cheatsheet.md +115 -0
- stogger-2026.5.12/docs/user/getting_started.md +93 -0
- stogger-2026.5.12/docs/user/index.md +13 -0
- stogger-2026.5.12/docs/user/logging_patterns.md +429 -0
- stogger-2026.5.12/docs/user/nix_integration.md +79 -0
- stogger-2026.5.12/docs/user/postgres.md +187 -0
- stogger-2026.5.12/docs/user/systemd.md +180 -0
- stogger-2026.5.12/docs/user/testing.md +156 -0
- stogger-2026.5.12/examples/demo/pyproject.toml +35 -0
- stogger-2026.5.12/examples/demo/src/demo/__init__.py +3 -0
- stogger-2026.5.12/examples/demo/src/demo/__main__.py +11 -0
- stogger-2026.5.12/examples/demo/src/demo/cli/__init__.py +0 -0
- stogger-2026.5.12/examples/demo/src/demo/cli/commands.py +79 -0
- stogger-2026.5.12/examples/demo/src/demo/service/__init__.py +0 -0
- stogger-2026.5.12/examples/demo/src/demo/service/orders.py +91 -0
- stogger-2026.5.12/examples/demo/tests/test_conventions.py +90 -0
- stogger-2026.5.12/examples/demo/tests/test_coverage.py +57 -0
- stogger-2026.5.12/examples/demo/uv.lock +361 -0
- stogger-2026.5.12/flake.nix +366 -0
- stogger-2026.5.12/packages/stogger-postgres/pyproject.toml +24 -0
- stogger-2026.5.12/packages/stogger-postgres/src/stogger_postgres/__init__.py +114 -0
- stogger-2026.5.12/pyproject.toml +196 -0
- stogger-2026.5.12/scripts/extract_logs.py +57 -0
- stogger-2026.5.12/src/stogger/__init__.py +72 -0
- stogger-2026.5.12/src/stogger/_colors.py +23 -0
- stogger-2026.5.12/src/stogger/_decorators.py +478 -0
- stogger-2026.5.12/src/stogger/_regexes.py +20 -0
- stogger-2026.5.12/src/stogger/_types.py +17 -0
- stogger-2026.5.12/src/stogger/config.py +660 -0
- stogger-2026.5.12/src/stogger/core.py +1035 -0
- stogger-2026.5.12/src/stogger/factory.py +234 -0
- stogger-2026.5.12/src/stogger/processors.py +29 -0
- stogger-2026.5.12/stubs/stogger_systemd/__init__.pyi +20 -0
- stogger-2026.5.12/tests/__init__.py +1 -0
- stogger-2026.5.12/tests/conftest.py +15 -0
- stogger-2026.5.12/tests/impl_spec/__init__.py +0 -0
- stogger-2026.5.12/tests/impl_spec/test_format_config_extension.py +373 -0
- stogger-2026.5.12/tests/impl_spec/test_logging_decorators.py +487 -0
- stogger-2026.5.12/tests/impl_spec/test_postgres_target.py +327 -0
- stogger-2026.5.12/tests/impl_spec/test_stogger_self_logging.py +337 -0
- stogger-2026.5.12/tests/test_architecture.py +57 -0
- stogger-2026.5.12/tests/test_config.py +454 -0
- stogger-2026.5.12/tests/test_core.py +869 -0
- stogger-2026.5.12/tests/test_decorators.py +234 -0
- stogger-2026.5.12/tests/test_e2e_single_module_app.py +167 -0
- stogger-2026.5.12/tests/test_exception_logging.py +23 -0
- stogger-2026.5.12/tests/test_factory.py +360 -0
- stogger-2026.5.12/tests/test_integration.py +172 -0
- stogger-2026.5.12/tests/test_postgres_integration.py +187 -0
- stogger-2026.5.12/tests/test_postgres_integration_real.py +38 -0
- stogger-2026.5.12/tests/test_systemd_integration.py +153 -0
- stogger-2026.5.12/tests/test_systemd_integration_real.py +35 -0
- stogger-2026.5.12/uv.lock +1340 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
---
|
|
2
|
+
lifecycle:
|
|
3
|
+
requirements:
|
|
4
|
+
completed_at: "2026-05-03T18:00:00Z"
|
|
5
|
+
git_rev: c4f0f2b
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Problem
|
|
9
|
+
|
|
10
|
+
pytest-stogger erzwingt 12 AST-basierte Logging-Konventionen. Basierend auf einem Sample von ~280 Log-Statements aus 3 Projekten (batou-type, code-rag, docdisco) wurden 6 Lücken identifiziert, die zu unnötigem Log-Noise, fehlendem Context und ungenutztem Structlog-Potential führen.
|
|
11
|
+
|
|
12
|
+
## Binding Decisions
|
|
13
|
+
|
|
14
|
+
### scope-batch
|
|
15
|
+
|
|
16
|
+
#### Context
|
|
17
|
+
|
|
18
|
+
Es gibt 6 vorgeschlagene Checks. Zu viele auf einmal risking Qualität, zu wenige liefern nicht den erwarteten Mehrwert.
|
|
19
|
+
|
|
20
|
+
#### Decision
|
|
21
|
+
|
|
22
|
+
Alle 6 Checks in einem Durchgang umsetzen:
|
|
23
|
+
1. `log-consolidate-repeated` — 3+ Log-Calls in direkter Folge mit shared Context → zusammenfassen
|
|
24
|
+
2. `log-debug-context-range` — max 5 key-value pairs bei DEBUG
|
|
25
|
+
3. `log-bind-threshold` — wiederholter key-value 3+ Mal → log.bind() nutzen
|
|
26
|
+
4. `log-exception-not-in-except` — log.exception() außerhalb except-Block
|
|
27
|
+
5. `log-error-in-except` — log.error() in except-Block statt log.exception()
|
|
28
|
+
6. `log-warning-for-not-found` — WARNING bei "not found" Event-IDs
|
|
29
|
+
|
|
30
|
+
#### Alternatives
|
|
31
|
+
|
|
32
|
+
a. Nur Top 3 — schneller, aber Lücken bleiben
|
|
33
|
+
b. Nur 1 MVP — zu wenig Impact
|
|
34
|
+
|
|
35
|
+
#### Consequences
|
|
36
|
+
|
|
37
|
+
Größerer Durchgang, aber alle Checks folgen demselben Registry-Pattern und teilen Helper-Funktionen.
|
|
38
|
+
|
|
39
|
+
### consolidate-severity
|
|
40
|
+
|
|
41
|
+
#### Context
|
|
42
|
+
|
|
43
|
+
Die Consolidation-Heuristik hat False Positives bei legitimen Multi-Step-Logging.
|
|
44
|
+
|
|
45
|
+
#### Decision
|
|
46
|
+
|
|
47
|
+
Config-gesteuert: Default WARNING, kann per Config auf ERROR gestellt werden via `consolidate_as_error = true`.
|
|
48
|
+
|
|
49
|
+
#### Alternatives
|
|
50
|
+
|
|
51
|
+
a. Immer WARNING — zu schwach für CI
|
|
52
|
+
b. Immer ERROR — zu viele False Positives
|
|
53
|
+
|
|
54
|
+
#### Consequences
|
|
55
|
+
|
|
56
|
+
Neue Config-Option `consolidate_as_error` in `[tool.pytest-stogger]`.
|
|
57
|
+
|
|
58
|
+
### debug-range-config
|
|
59
|
+
|
|
60
|
+
#### Context
|
|
61
|
+
|
|
62
|
+
Konvention ist "2-3 key-values normal, max 5". Braucht das einen konfigurierbaren Schwellwert?
|
|
63
|
+
|
|
64
|
+
#### Decision
|
|
65
|
+
|
|
66
|
+
Fester Wert 5 — hardcoded wie die meisten Rules. Kein Config-Overhead.
|
|
67
|
+
|
|
68
|
+
#### Alternatives
|
|
69
|
+
|
|
70
|
+
a. Per Config (debug_max_context) — flexibel aber unnötig
|
|
71
|
+
b. Nur Warning bei >7 — zu großzügig
|
|
72
|
+
|
|
73
|
+
#### Consequences
|
|
74
|
+
|
|
75
|
+
Hardgrenze bei 5 key-value pairs für DEBUG. Darüber → Violation.
|
|
76
|
+
|
|
77
|
+
### exception-placement
|
|
78
|
+
|
|
79
|
+
#### Context
|
|
80
|
+
|
|
81
|
+
log.exception() erzeugt einen Stacktrace — ohne except-Block ist das sinnlos oder verwirrend.
|
|
82
|
+
|
|
83
|
+
#### Decision
|
|
84
|
+
|
|
85
|
+
Eigenständiger Check: `log-exception-not-in-except`. log.exception() nur erlaubt innerhalb von except-Blöcken.
|
|
86
|
+
|
|
87
|
+
#### Alternatives
|
|
88
|
+
|
|
89
|
+
a. Mit Inline-Ignore — nicht nötig, klarer Fix
|
|
90
|
+
b. Nicht umsetzen — zu noisy
|
|
91
|
+
|
|
92
|
+
#### Consequences
|
|
93
|
+
|
|
94
|
+
Klare Rule mit klarer Migration: exception() → error() wenn außerhalb von except.
|
|
95
|
+
|
|
96
|
+
### consolidate-message-ux
|
|
97
|
+
|
|
98
|
+
#### Context
|
|
99
|
+
|
|
100
|
+
Fehlermeldungen müssen dem User helfen, nicht nur informieren.
|
|
101
|
+
|
|
102
|
+
#### Decision
|
|
103
|
+
|
|
104
|
+
Zeige Merge-Vorschlag: betroffene Zeilen + konkreter Vorschlag wie die zusammengefasste Statement aussehen könnte.
|
|
105
|
+
|
|
106
|
+
#### Alternatives
|
|
107
|
+
|
|
108
|
+
a. Nur Zeilen + Count — nicht hilfreich genug
|
|
109
|
+
b. Diff-Style — zu komplex für ersten Wurf
|
|
110
|
+
|
|
111
|
+
#### Consequences
|
|
112
|
+
|
|
113
|
+
Die Violation-Message enthält ein konkretes Code-Beispiel.
|
|
114
|
+
|
|
115
|
+
## Scope Boundaries
|
|
116
|
+
|
|
117
|
+
- INCLUDE: 6 neue File-Rules im bestehenden Registry-Pattern
|
|
118
|
+
- INCLUDE: Neue Config-Option `consolidate_as_error`
|
|
119
|
+
- EXCLUDE: Changes an bestehenden Rules
|
|
120
|
+
- EXCLUDE: Changes am logging-coverage Check
|
|
121
|
+
- EXCLUDE: Autofix-Funktionalität (nur Detection)
|
|
122
|
+
|
|
123
|
+
## Open Questions
|
|
124
|
+
|
|
125
|
+
- Severity der restlichen 5 Checks (ERROR wie bestehende Rules?)
|
|
126
|
+
- Default-Severity für bind-threshold und error-in-except
|
|
127
|
+
- Interface: wie werden die neuen Rules in --help und Status-Output sichtbar?
|
|
128
|
+
|
|
129
|
+
### severity-strategy
|
|
130
|
+
|
|
131
|
+
#### Context
|
|
132
|
+
|
|
133
|
+
6 neue Checks mit unterschiedlicher Heuristik-Qualität. Klare AST-basierte Rules können ERROR sein, heuristische sollten WARNING sein.
|
|
134
|
+
|
|
135
|
+
#### Decision
|
|
136
|
+
|
|
137
|
+
Gemischt:
|
|
138
|
+
- ERROR: `log-exception-not-in-except`, `log-error-in-except`, `log-debug-context-range`, `log-bind-threshold`
|
|
139
|
+
- WARNING (config-steuerbar): `log-consolidate-repeated`, `log-warning-for-not-found`
|
|
140
|
+
|
|
141
|
+
#### Alternatives
|
|
142
|
+
|
|
143
|
+
a. Alle ERROR — zu streng für heuristische Checks
|
|
144
|
+
b. Alle WARNING — zu schwach für klare Rules
|
|
145
|
+
|
|
146
|
+
#### Consequences
|
|
147
|
+
|
|
148
|
+
Zwei Severity-Level in der Rule-Architektur. Needs flag `needs_warning_severity` auf _RuleSpec oder Config-basiert.
|
|
149
|
+
|
|
150
|
+
### error-in-except-severity
|
|
151
|
+
|
|
152
|
+
#### Context
|
|
153
|
+
|
|
154
|
+
log.error() in except-Block verliert den Stacktrace — suboptimal aber nicht falsch.
|
|
155
|
+
|
|
156
|
+
#### Decision
|
|
157
|
+
|
|
158
|
+
ERROR — strenger als ursprünglich empfohlen. log.exception() ist in except-Blöcken immer die bessere Wahl.
|
|
159
|
+
|
|
160
|
+
#### Alternatives
|
|
161
|
+
|
|
162
|
+
a. WARNING — weniger Breakage aber schwächer
|
|
163
|
+
b. Nicht umsetzen — bestehende no-log-info-in-except reicht
|
|
164
|
+
|
|
165
|
+
#### Consequences
|
|
166
|
+
|
|
167
|
+
Migration: alle log.error() in except-Blöcken → log.exception(). Inline-Ignore möglich.
|
|
168
|
+
|
|
169
|
+
### warning-not-found-fp
|
|
170
|
+
|
|
171
|
+
#### Context
|
|
172
|
+
|
|
173
|
+
Pattern-Matching auf Event-IDs (*-not-found, *-missing, *-absent) hat False Positives.
|
|
174
|
+
|
|
175
|
+
#### Decision
|
|
176
|
+
|
|
177
|
+
Inline-Ignore: # stogger: ignore log-warning-for-not-found für legitime Fälle.
|
|
178
|
+
|
|
179
|
+
#### Alternatives
|
|
180
|
+
|
|
181
|
+
a. Exempt-List in Config — flexibel aber Config-Bloat
|
|
182
|
+
b. Nicht umsetzen — zu heuristisch
|
|
183
|
+
|
|
184
|
+
#### Consequences
|
|
185
|
+
|
|
186
|
+
Neue Rule mit needs_inline_ignores=True Flag.
|
|
187
|
+
|
|
188
|
+
### bind-threshold-value
|
|
189
|
+
|
|
190
|
+
#### Context
|
|
191
|
+
|
|
192
|
+
Wiederholter key-value in Scope → log.bind() nutzen.
|
|
193
|
+
|
|
194
|
+
#### Decision
|
|
195
|
+
|
|
196
|
+
Schwellwert 3 — konsistent mit bestehender check_bind_for_repeating die auch bei 3 Wiederholungen triggert.
|
|
197
|
+
|
|
198
|
+
#### Alternatives
|
|
199
|
+
|
|
200
|
+
a. 2 Mal — strenger
|
|
201
|
+
b. Config-gesteuert — unnötig, 3 ist bewährt
|
|
202
|
+
|
|
203
|
+
#### Consequences
|
|
204
|
+
|
|
205
|
+
Konsistente Schwelle im Codebase. Kein neuer Config-Wert nötig.
|
|
206
|
+
|
|
207
|
+
## Scope Boundaries (Updated)
|
|
208
|
+
|
|
209
|
+
- INCLUDE: 6 neue File-Rules im bestehenden Registry-Pattern
|
|
210
|
+
- INCLUDE: Neue Config-Option `consolidate_as_error`
|
|
211
|
+
- INCLUDE: Severity-Level ERROR vs WARNING für neue Rules
|
|
212
|
+
- EXCLUDE: Changes an bestehenden Rules
|
|
213
|
+
- EXCLUDE: Changes am logging-coverage Check
|
|
214
|
+
- EXCLUDE: Autofix-Funktionalität (nur Detection)
|
|
215
|
+
|
|
216
|
+
### warning-pytest-output
|
|
217
|
+
|
|
218
|
+
#### Context
|
|
219
|
+
|
|
220
|
+
WARNING-Level Rules sollen den Test nicht failen lassen, aber sichtbar sein.
|
|
221
|
+
|
|
222
|
+
#### Decision
|
|
223
|
+
|
|
224
|
+
Normale `pytest.warning` — Test besteht, Warning wird angezeigt. Konsistent mit Python-Warnings.
|
|
225
|
+
|
|
226
|
+
#### Alternatives
|
|
227
|
+
|
|
228
|
+
a. Eigener Item-Typ — zu komplex
|
|
229
|
+
b. Nur in Summary — nicht prominent genug
|
|
230
|
+
|
|
231
|
+
#### Consequences
|
|
232
|
+
|
|
233
|
+
Plugin nutzt `warnings.warn()` statt `StoggerViolationError` für WARNING-Level Rules.
|
|
234
|
+
|
|
235
|
+
### warning-flag-architecture
|
|
236
|
+
|
|
237
|
+
#### Context
|
|
238
|
+
|
|
239
|
+
Rule-Architektur braucht ein Konzept für Severity-Level.
|
|
240
|
+
|
|
241
|
+
#### Decision
|
|
242
|
+
|
|
243
|
+
Neues Flag `is_warning: bool = False` auf `_RuleSpec`. Plugin entscheidet basierend auf Flag ob ERROR oder WARNING.
|
|
244
|
+
|
|
245
|
+
#### Alternatives
|
|
246
|
+
|
|
247
|
+
a. Config pro Rule — flexibel aber Config-Bloat
|
|
248
|
+
b. Separate Rule-Liste — dupliziert Architektur
|
|
249
|
+
|
|
250
|
+
#### Consequences
|
|
251
|
+
|
|
252
|
+
Neues Feld auf _RuleSpec. Plugin.py muss zwei Pfade haben: ERROR-Path (bestehend) + WARNING-Path (neu, via warnings.warn).
|
|
253
|
+
|
|
254
|
+
### discoverability
|
|
255
|
+
|
|
256
|
+
#### Context
|
|
257
|
+
|
|
258
|
+
Neue Rules sollen für Consumer-Projekte sofort nützlich sein.
|
|
259
|
+
|
|
260
|
+
#### Decision
|
|
261
|
+
|
|
262
|
+
Automatisch sichtbar — Zero-Config. Neue Rules laufen wie bestehende, werden im Test-Output sichtbar.
|
|
263
|
+
|
|
264
|
+
#### Alternatives
|
|
265
|
+
|
|
266
|
+
a. Disabled per Default — sicher aber weniger nützlich
|
|
267
|
+
|
|
268
|
+
#### Consequences
|
|
269
|
+
|
|
270
|
+
Consumer-Projekte sehen neue Rules sofort. WARNING-Level Rules brechen nicht, ERROR-Level Rules können per disable_rules deaktiviert werden.
|
|
271
|
+
|
|
272
|
+
## Scope Boundaries (Final)
|
|
273
|
+
|
|
274
|
+
- INCLUDE: 6 neue File-Rules im bestehenden Registry-Pattern
|
|
275
|
+
- INCLUDE: Neues `is_warning` Flag auf `_RuleSpec`
|
|
276
|
+
- INCLUDE: WARNING-Path via `warnings.warn()` in plugin.py
|
|
277
|
+
- INCLUDE: Neue Config-Option `consolidate_as_error`
|
|
278
|
+
- EXCLUDE: Changes an bestehenden Rules
|
|
279
|
+
- EXCLUDE: Changes am logging-coverage Check
|
|
280
|
+
- EXCLUDE: Autofix-Funktionalität (nur Detection)
|
|
281
|
+
|
|
282
|
+
- Interface: wie werden die neuen Rules in --help und Status-Output sichtbar?
|
|
283
|
+
- Brauchen wir ein neues `needs_warning_severity` Flag auf `_RuleSpec`?
|
|
284
|
+
- Wie wird WARNING im pytest-Output dargestellt? ( eigenes Item-Type oder nur in Summary?)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
lifecycle:
|
|
3
|
+
requirements:
|
|
4
|
+
completed_at: "2026-05-04T14:00:00Z"
|
|
5
|
+
git_rev: "be50a11"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# logging-decorators-docs
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
The logging decorators (`log_call`, `log_result`, `log_operation`, `log_scope`) are implemented and functional (commit be50a11) but documentation-invisible. Docstrings lack structured parameter docs. Autoapi skips `_decorators.py` (underscore prefix). No user-facing prose exists. Users cannot discover or learn the decorators through docs.
|
|
13
|
+
|
|
14
|
+
## Decisions
|
|
15
|
+
|
|
16
|
+
### docstring-quality
|
|
17
|
+
|
|
18
|
+
#### Context
|
|
19
|
+
|
|
20
|
+
Existing docstrings describe behavior in 1-2 sentences but omit structured parameter documentation, return types, raised exceptions, and usage examples. Other stogger public APIs (e.g., `init_logging`) have full Args/Returns/Raises docstrings.
|
|
21
|
+
|
|
22
|
+
#### Decision
|
|
23
|
+
|
|
24
|
+
Enrich all 5 public docstrings (`log_call`, `log_result`, `log_operation`, `log_scope`, `LogScope`) with Google-style Args, Returns, Raises, and Examples sections. Match the quality of `init_logging` in `core.py`.
|
|
25
|
+
|
|
26
|
+
#### Alternatives
|
|
27
|
+
|
|
28
|
+
a. Numpy-style docstrings — inconsistent with existing codebase convention
|
|
29
|
+
b. Minimal docstrings + separate docs — forces users to look in two places
|
|
30
|
+
|
|
31
|
+
#### Consequences
|
|
32
|
+
|
|
33
|
+
`help(log_call)` and generated API docs both show complete usage information. Single source of truth per function.
|
|
34
|
+
|
|
35
|
+
### api-docs-visibility
|
|
36
|
+
|
|
37
|
+
#### Context
|
|
38
|
+
|
|
39
|
+
Autoapi skips `_decorators.py` because the `autoapi_skip_member` hook in `conf.py` rejects names starting with `_`. The re-exports in `core.py` are bare imports (`# noqa: F401`) so autoapi doesn't document them either. Generated `docs/api/stogger/core/index.rst` has zero decorator entries.
|
|
40
|
+
|
|
41
|
+
#### Decision
|
|
42
|
+
|
|
43
|
+
Add manual Sphinx directives to `docs/api/stogger/core/index.rst` for the 5 re-exported names. Use `.. py:function::` for decorators and `.. py:class::` for `LogScope`, with `:canonical:` pointing to `stogger._decorators` source. This keeps the private module hidden while surfacing the public API.
|
|
44
|
+
|
|
45
|
+
#### Alternatives
|
|
46
|
+
|
|
47
|
+
a. Modify `autoapi_skip_member` to allow `_decorators` — exposes private module internals
|
|
48
|
+
b. Move decorators to public `decorators.py` — violates architecture decision (Layer 2 placement)
|
|
49
|
+
|
|
50
|
+
#### Consequences
|
|
51
|
+
|
|
52
|
+
Decorators appear in generated HTML docs under `stogger.core`. Source links go to `_decorators.py` which is acceptable since users navigate via public API, not module internals.
|
|
53
|
+
|
|
54
|
+
### user-guide-integration
|
|
55
|
+
|
|
56
|
+
#### Context
|
|
57
|
+
|
|
58
|
+
`docs/user/logging_patterns.md` has manual "Function Tracing" and "Timing Operations" patterns that are exactly what the decorators automate. Users reading that page have no indication that decorators exist.
|
|
59
|
+
|
|
60
|
+
#### Decision
|
|
61
|
+
|
|
62
|
+
Add a new "Decorators" section to `logging_patterns.md` after the "Common Patterns" section. Cover all 4 features (`@log_call`, `@log_result`, `@log_operation`, `log_scope()`) with practical examples. Cross-reference from the existing "Function Tracing" and "Timing Operations" subsections.
|
|
63
|
+
|
|
64
|
+
#### Alternatives
|
|
65
|
+
|
|
66
|
+
a. New standalone page `docs/user/decorators.md` — fragments the logging patterns narrative
|
|
67
|
+
b. No user guide, rely on API docs only — poor discoverability for new users
|
|
68
|
+
|
|
69
|
+
#### Consequences
|
|
70
|
+
|
|
71
|
+
Users reading logging patterns naturally discover decorators. Single coherent page covers manual and automated approaches.
|
|
72
|
+
|
|
73
|
+
## Requirements
|
|
74
|
+
|
|
75
|
+
### Files to Modify
|
|
76
|
+
|
|
77
|
+
- **Modify**: `src/stogger/_decorators.py` — enriched docstrings for all 5 public names
|
|
78
|
+
- **Modify**: `docs/api/stogger/core/index.rst` — add decorator entries to summary lists and module contents
|
|
79
|
+
- **Modify**: `docs/user/logging_patterns.md` — add "Decorators" section with examples
|
|
80
|
+
|
|
81
|
+
### Docstring Requirements
|
|
82
|
+
|
|
83
|
+
Each decorator must document: Args (func, include_args, exclude_args with types and semantics), event format (fields emitted), exception behavior. `LogScope` must document constructor args, `add_fields`, enter/exit lifecycle, async support. `log_scope` must document name and **fields parameters. All must include a 2-3 line usage example.
|
|
84
|
+
|
|
85
|
+
### User Guide Requirements
|
|
86
|
+
|
|
87
|
+
New section must show: basic usage of all 4 features, arg filtering example, exception handling example, async usage. Each example must be self-contained and runnable.
|
|
88
|
+
|
|
89
|
+
## References
|
|
90
|
+
|
|
91
|
+
- `.agents/impl_specs/logging-decorators.md` — original implementation spec with event schemas and interface contracts
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
---
|
|
2
|
+
lifecycle:
|
|
3
|
+
requirements:
|
|
4
|
+
completed_at: "2026-05-03T12:00:00Z"
|
|
5
|
+
git_rev: "c4f0f2b"
|
|
6
|
+
design:
|
|
7
|
+
completed_at: "2026-05-03T12:30:00Z"
|
|
8
|
+
git_rev: "c4f0f2b"
|
|
9
|
+
implement:
|
|
10
|
+
completed_at: "2026-05-03T13:00:00Z"
|
|
11
|
+
git_rev: "1682052"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# logging-decorators
|
|
15
|
+
|
|
16
|
+
## Context
|
|
17
|
+
|
|
18
|
+
Stogger has no decorators or context managers for structured logging. Users must manually write `log.info("called", func=..., args={...})`. Three named decorators and one context manager will provide structured call/result logging that integrates with the existing structlog pipeline.
|
|
19
|
+
|
|
20
|
+
## Decisions
|
|
21
|
+
|
|
22
|
+
### module-placement
|
|
23
|
+
|
|
24
|
+
#### Context
|
|
25
|
+
|
|
26
|
+
New code must fit the existing layered architecture enforced by pytest-archon. core.py is already 951 lines and should not grow further.
|
|
27
|
+
|
|
28
|
+
#### Decision
|
|
29
|
+
|
|
30
|
+
New private module `src/stogger/_decorators.py` at Layer 2. Imports `_types` and `structlog`. Re-exported through `core.py` → `__init__.py`. Registered in `test_architecture.py` with Layer 2 rules (may import `_types`, `config`; may not import `factory` or `__init__`).
|
|
31
|
+
|
|
32
|
+
#### Alternatives
|
|
33
|
+
|
|
34
|
+
a. Add to core.py — would push it past 1100 lines, no separation of concerns
|
|
35
|
+
b. Public `decorators.py` at Layer 1 — cannot import `_types`, would need duplicate EventDict
|
|
36
|
+
|
|
37
|
+
#### Consequences
|
|
38
|
+
|
|
39
|
+
Clean module boundary. Architecture enforcement covers the new code from day one.
|
|
40
|
+
|
|
41
|
+
### decorator-variants
|
|
42
|
+
|
|
43
|
+
#### Context
|
|
44
|
+
|
|
45
|
+
Users need different granularity levels for function-call logging. Three named decorators are more discoverable than one configurable decorator.
|
|
46
|
+
|
|
47
|
+
#### Decision
|
|
48
|
+
|
|
49
|
+
Three decorators with distinct semantics:
|
|
50
|
+
|
|
51
|
+
1. `@log_call` — logs at entry. Event: `event="called"`, `func="module.qualname"`, `args={...}`. No result, no duration.
|
|
52
|
+
2. `@log_result` — logs at exit. Event: `event="returned"`, `func="module.qualname"`, `result=...`, `duration_ms=...`. No args.
|
|
53
|
+
3. `@log_operation` — logs at exit. Event: `event="operation"`, `func="module.qualname"`, `args={...}`, `result=...`, `duration_ms=...`. Everything in one event.
|
|
54
|
+
|
|
55
|
+
All three share: sync+async support, `include_args`/`exclude_args` filtering, auto-logger via `structlog.get_logger()`, exception logging with re-raise, automatic `self`/`cls` filtering.
|
|
56
|
+
|
|
57
|
+
#### Alternatives
|
|
58
|
+
|
|
59
|
+
a. Single decorator with mode parameter — less discoverable, obscures intent
|
|
60
|
+
b. Eliot-style start/end event pairs — doubles output, adds complexity
|
|
61
|
+
|
|
62
|
+
#### Consequences
|
|
63
|
+
|
|
64
|
+
Three explicit import paths. Users choose by intent: invocation tracking (`log_call`), result tracking (`log_result`), full audit (`log_operation`).
|
|
65
|
+
|
|
66
|
+
### scope-object-design
|
|
67
|
+
|
|
68
|
+
#### Context
|
|
69
|
+
|
|
70
|
+
The `log_scope()` context manager needs state (start time, bound fields, accumulated fields) and explicit enter/exit behavior including exception handling.
|
|
71
|
+
|
|
72
|
+
#### Decision
|
|
73
|
+
|
|
74
|
+
`LogScope` class with `__enter__`/`__exit__`. On enter: binds fields via structlog context. On exit: logs `scope_end` event with duration and accumulated fields. On exception: logs `scope_failed` event with `exc_type`/`exc_msg`, then re-raises. `add_fields(**kwargs)` method for mid-scope enrichment. Factory function `log_scope(name, **fields)` returns a `LogScope` instance.
|
|
75
|
+
|
|
76
|
+
Async support via `__aenter__`/`__aexit__` on the same class.
|
|
77
|
+
|
|
78
|
+
#### Alternatives
|
|
79
|
+
|
|
80
|
+
a. Generator-based `@contextmanager` — requires separate `@asynccontextmanager` for async
|
|
81
|
+
b. Two separate classes for sync/async — doubles code
|
|
82
|
+
|
|
83
|
+
#### Consequences
|
|
84
|
+
|
|
85
|
+
Single class handles both sync and async. Stateful object enables `add_fields()` accumulation.
|
|
86
|
+
|
|
87
|
+
### async-support
|
|
88
|
+
|
|
89
|
+
#### Context
|
|
90
|
+
|
|
91
|
+
Python ≥3.14. All decorators and context manager must work with both sync and async functions.
|
|
92
|
+
|
|
93
|
+
#### Decision
|
|
94
|
+
|
|
95
|
+
Each decorator uses `inspect.iscoroutinefunction(wrapped_function)` at decoration time. Returns either a sync wrapper or an async wrapper accordingly. No runtime check overhead — the check happens once at import/decoration time.
|
|
96
|
+
|
|
97
|
+
For `LogScope`: implements both `__enter__`/`__exit__` and `__aenter__`/`__aexit__`. Python automatically calls the correct protocol based on `with` vs `async with`.
|
|
98
|
+
|
|
99
|
+
#### Alternatives
|
|
100
|
+
|
|
101
|
+
a. Single sync wrapper — breaks async functions
|
|
102
|
+
b. Runtime isinstance(result, Coroutine) check — overhead on every call
|
|
103
|
+
|
|
104
|
+
#### Consequences
|
|
105
|
+
|
|
106
|
+
Zero runtime overhead for the sync/async dispatch. Two code paths per decorator, but they share arg-extraction and logging logic.
|
|
107
|
+
|
|
108
|
+
### arg-extraction
|
|
109
|
+
|
|
110
|
+
#### Context
|
|
111
|
+
|
|
112
|
+
Need `args={'x': 1, 'y': 2}` in events. `inspect.getcallargs` is removed in Python 3.14.
|
|
113
|
+
|
|
114
|
+
#### Decision
|
|
115
|
+
|
|
116
|
+
`inspect.signature(wrapped_function)` + `sig.bind(*args, **kwargs)` + `bound.apply_defaults()`. This resolves positional args, keyword args, and defaults into a plain dict. Then apply `include_args`/`exclude_args` filtering and strip `self`/`cls`.
|
|
117
|
+
|
|
118
|
+
#### Alternatives
|
|
119
|
+
|
|
120
|
+
a. Manual dict construction — loses parameter names and defaults
|
|
121
|
+
b. `locals()` capture — unreliable with decorators
|
|
122
|
+
|
|
123
|
+
#### Consequences
|
|
124
|
+
|
|
125
|
+
Correct arg resolution including defaults. Standard pattern that works with Python 3.14+.
|
|
126
|
+
|
|
127
|
+
### duration-measurement
|
|
128
|
+
|
|
129
|
+
#### Context
|
|
130
|
+
|
|
131
|
+
`@log_result`, `@log_operation`, and `log_scope()` all need `duration_ms`.
|
|
132
|
+
|
|
133
|
+
#### Decision
|
|
134
|
+
|
|
135
|
+
`time.perf_counter()` — capture `t0` at entry, compute `(perf_counter() - t0) * 1000` at exit. Highest resolution, monotonic, no NTP drift.
|
|
136
|
+
|
|
137
|
+
#### Alternatives
|
|
138
|
+
|
|
139
|
+
a. `time.monotonic` — practically equivalent on Linux, lower resolution on some platforms
|
|
140
|
+
b. Event timestamp subtraction — impossible for single-event decorators like `@log_call`
|
|
141
|
+
|
|
142
|
+
#### Consequences
|
|
143
|
+
|
|
144
|
+
Consistent duration measurement across all features. Sub-millisecond precision.
|
|
145
|
+
|
|
146
|
+
### exception-handling
|
|
147
|
+
|
|
148
|
+
#### Context
|
|
149
|
+
|
|
150
|
+
Exceptions in decorated functions must be logged without changing program behavior.
|
|
151
|
+
|
|
152
|
+
#### Decision
|
|
153
|
+
|
|
154
|
+
Own `exc_type` and `exc_msg` fields in the event dict. `exc_type` = exception class name (e.g., `"ValueError"`). `exc_msg` = `str(exception)`. Exception is then re-raised unchanged. Does NOT use `exc_info=True` — the existing `process_exc_info`/`format_exc_info` pipeline is not invoked for decorator exceptions.
|
|
155
|
+
|
|
156
|
+
Event semantics per decorator on exception:
|
|
157
|
+
- `@log_call` — no exception handling (logs at entry, before execution)
|
|
158
|
+
- `@log_result` — `event="failed"`, `exc_type`, `exc_msg`, `duration_ms`
|
|
159
|
+
- `@log_operation` — `event="failed"`, `exc_type`, `exc_msg`, `duration_ms`, `args`
|
|
160
|
+
- `log_scope()` — `event="scope_failed"`, `exc_type`, `exc_msg`, `duration_ms`
|
|
161
|
+
|
|
162
|
+
#### Alternatives
|
|
163
|
+
|
|
164
|
+
a. `exc_info=True` via existing pipeline — produces full tracebacks, too verbose for every decorated call
|
|
165
|
+
b. Both own fields AND exc_info — redundant output
|
|
166
|
+
|
|
167
|
+
#### Consequences
|
|
168
|
+
|
|
169
|
+
Compact exception logging. Users get exception type and message without traceback noise. Full tracebacks available via explicit `log.exception()` outside decorators.
|
|
170
|
+
|
|
171
|
+
### func-name
|
|
172
|
+
|
|
173
|
+
#### Context
|
|
174
|
+
|
|
175
|
+
Every event needs a `func` field identifying the decorated function.
|
|
176
|
+
|
|
177
|
+
#### Decision
|
|
178
|
+
|
|
179
|
+
`f"{wrapped_function.__module__}.{wrapped_function.__qualname__}"` at decoration time. Stored once, no runtime inspection needed. Works with `@functools.wraps` which preserves `__module__` and `__qualname__`.
|
|
180
|
+
|
|
181
|
+
#### Alternatives
|
|
182
|
+
|
|
183
|
+
a. `f.__name__` only — not unique across modules
|
|
184
|
+
b. Frame inspection — expensive, unreliable with decorators
|
|
185
|
+
|
|
186
|
+
#### Consequences
|
|
187
|
+
|
|
188
|
+
Unique, searchable function identifiers. Zero runtime cost (computed once at decoration).
|
|
189
|
+
|
|
190
|
+
### test-strategy
|
|
191
|
+
|
|
192
|
+
#### Context
|
|
193
|
+
|
|
194
|
+
No shared test fixtures exist. Each test file has its own `_reset_structlog` autouse fixture.
|
|
195
|
+
|
|
196
|
+
#### Decision
|
|
197
|
+
|
|
198
|
+
1. New `tests/conftest.py` with shared `_reset_structlog` autouse fixture (call `structlog.reset_defaults()`).
|
|
199
|
+
2. New `tests/test_decorators.py` with tests for all three decorators and `log_scope()`.
|
|
200
|
+
3. Update `tests/test_architecture.py` to register `_decorators` as Layer 2 module.
|
|
201
|
+
4. TDD approach: write tests first, then implementation.
|
|
202
|
+
|
|
203
|
+
Test locations:
|
|
204
|
+
- Permanent decision tests: `tests/test_decorators.py`
|
|
205
|
+
- Architecture enforcement: `tests/test_architecture.py`
|
|
206
|
+
|
|
207
|
+
#### Alternatives
|
|
208
|
+
|
|
209
|
+
a. No shared conftest, each file has its own reset — inconsistent with the improvement opportunity
|
|
210
|
+
b. No new tests — unacceptable for new public API
|
|
211
|
+
|
|
212
|
+
#### Consequences
|
|
213
|
+
|
|
214
|
+
Solid test foundation. Architecture rules cover the new module from the start.
|
|
215
|
+
|
|
216
|
+
## Requirements
|
|
217
|
+
|
|
218
|
+
### Interface Contracts
|
|
219
|
+
|
|
220
|
+
**Decorator usage:**
|
|
221
|
+
```python
|
|
222
|
+
from stogger import log_call, log_result, log_operation
|
|
223
|
+
|
|
224
|
+
@log_call
|
|
225
|
+
def fetch_user(user_id: int): ...
|
|
226
|
+
|
|
227
|
+
@log_result
|
|
228
|
+
def compute_hash(data: bytes) -> str: ...
|
|
229
|
+
|
|
230
|
+
@log_operation(include_args=["query"], exclude_args=["password"])
|
|
231
|
+
def authenticate(query: str, password: str) -> bool: ...
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Context manager usage:**
|
|
235
|
+
```python
|
|
236
|
+
from stogger import log_scope
|
|
237
|
+
|
|
238
|
+
with log_scope("db_transaction", table="users") as scope:
|
|
239
|
+
insert(user)
|
|
240
|
+
scope.add_fields(rows_inserted=1)
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Event examples:**
|
|
244
|
+
```
|
|
245
|
+
# @log_call
|
|
246
|
+
{"event": "called", "func": "mymodule.fetch_user", "args": {"user_id": 42}}
|
|
247
|
+
|
|
248
|
+
# @log_result
|
|
249
|
+
{"event": "returned", "func": "mymodule.compute_hash", "result": "abc123", "duration_ms": 2.4}
|
|
250
|
+
|
|
251
|
+
# @log_operation
|
|
252
|
+
{"event": "operation", "func": "mymodule.authenticate", "args": {"query": "admin"}, "result": true, "duration_ms": 15.3}
|
|
253
|
+
|
|
254
|
+
# log_scope success
|
|
255
|
+
{"event": "scope_end", "scope": "db_transaction", "table": "users", "rows_inserted": 1, "duration_ms": 45.2}
|
|
256
|
+
|
|
257
|
+
# @log_result exception
|
|
258
|
+
{"event": "failed", "func": "mymodule.compute_hash", "exc_type": "ValueError", "exc_msg": "empty input", "duration_ms": 0.1}
|
|
259
|
+
|
|
260
|
+
# log_scope exception
|
|
261
|
+
{"event": "scope_failed", "scope": "db_transaction", "exc_type": "ConnectionError", "exc_msg": "timeout", "duration_ms": 30001.0}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Scope Boundary
|
|
265
|
+
|
|
266
|
+
Not part of this work: task_uuid, task_level, parent-child tracking, preserve_context, distributed tracing, serialization.
|
|
267
|
+
|
|
268
|
+
### Files to Create/Modify
|
|
269
|
+
|
|
270
|
+
- **Create**: `src/stogger/_decorators.py`
|
|
271
|
+
- **Modify**: `src/stogger/core.py` — add re-exports for decorators and log_scope
|
|
272
|
+
- **Modify**: `src/stogger/__init__.py` — add to `__all__` and import from core
|
|
273
|
+
- **Create**: `tests/conftest.py` — shared _reset_structlog fixture
|
|
274
|
+
- **Create**: `tests/test_decorators.py`
|
|
275
|
+
- **Modify**: `tests/test_architecture.py` — register _decorators as Layer 2
|
|
276
|
+
|
|
277
|
+
## Appendix
|
|
278
|
+
|
|
279
|
+
### Implementation Plan
|
|
280
|
+
|
|
281
|
+
```yaml
|
|
282
|
+
id: logging-decorators
|
|
283
|
+
description: "Three logging decorators (log_call, log_result, log_operation) and one context manager (log_scope) as new _decorators.py module at Layer 2. Sync+async support, arg filtering, duration measurement, exception logging."
|
|
284
|
+
specs:
|
|
285
|
+
- .agents/impl_specs/logging-decorators.md
|
|
286
|
+
target_tests:
|
|
287
|
+
- file: tests/impl_spec/test_logging_decorators.py
|
|
288
|
+
tests:
|
|
289
|
+
- test_import_log_call_from_decorators
|
|
290
|
+
- test_import_log_result_from_decorators
|
|
291
|
+
- test_import_log_operation_from_decorators
|
|
292
|
+
- test_import_log_scope_from_decorators
|
|
293
|
+
- test_import_log_call_from_stogger_top_level
|
|
294
|
+
- test_import_log_scope_from_stogger_top_level
|
|
295
|
+
- test_log_call_produces_called_event
|
|
296
|
+
- test_log_result_produces_returned_event
|
|
297
|
+
- test_log_operation_produces_operation_event
|
|
298
|
+
- test_log_scope_returns_log_scope_instance
|
|
299
|
+
- test_log_scope_has_add_fields_method
|
|
300
|
+
- test_log_scope_success_produces_scope_end_event
|
|
301
|
+
- test_log_scope_exception_produces_scope_failed_event
|
|
302
|
+
- test_log_call_works_with_async
|
|
303
|
+
- test_log_result_works_with_async
|
|
304
|
+
- test_log_operation_works_with_async
|
|
305
|
+
- test_log_scope_works_with_async_with
|
|
306
|
+
- test_args_include_default_values
|
|
307
|
+
- test_args_strip_self
|
|
308
|
+
- test_args_strip_cls
|
|
309
|
+
- test_include_args_whitelist_filtering
|
|
310
|
+
- test_exclude_args_blacklist_filtering
|
|
311
|
+
- test_log_result_has_duration_ms
|
|
312
|
+
- test_log_operation_has_duration_ms
|
|
313
|
+
- test_log_scope_has_duration_ms
|
|
314
|
+
- test_log_result_logs_exception_and_reraises
|
|
315
|
+
- test_log_operation_logs_exception_with_args
|
|
316
|
+
- test_log_call_does_not_catch_exceptions
|
|
317
|
+
- test_log_scope_exception_reraises
|
|
318
|
+
- test_func_field_is_module_qualname
|
|
319
|
+
- test_func_field_for_method
|
|
320
|
+
created_at: "2026-05-03T13:00:00Z"
|
|
321
|
+
git_rev: "1682052"
|
|
322
|
+
```
|