logging-objects-with-schema 1.0.0__tar.gz → 1.0.2__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.
- {logging_objects_with_schema-1.0.0 → logging_objects_with_schema-1.0.2}/PKG-INFO +21 -15
- {logging_objects_with_schema-1.0.0 → logging_objects_with_schema-1.0.2}/README.md +19 -13
- logging_objects_with_schema-1.0.2/pyproject.toml +97 -0
- logging_objects_with_schema-1.0.0/pyproject.toml → logging_objects_with_schema-1.0.2/pyproject.toml.orig +7 -37
- {logging_objects_with_schema-1.0.0 → logging_objects_with_schema-1.0.2}/src/logging_objects_with_schema/schema_loader.py +5 -4
- {logging_objects_with_schema-1.0.0 → logging_objects_with_schema-1.0.2}/src/logging_objects_with_schema/schema_logger.py +12 -11
- {logging_objects_with_schema-1.0.0 → logging_objects_with_schema-1.0.2}/LICENSE +0 -0
- {logging_objects_with_schema-1.0.0 → logging_objects_with_schema-1.0.2}/src/logging_objects_with_schema/__init__.py +0 -0
- {logging_objects_with_schema-1.0.0 → logging_objects_with_schema-1.0.2}/src/logging_objects_with_schema/errors.py +0 -0
- {logging_objects_with_schema-1.0.0 → logging_objects_with_schema-1.0.2}/src/logging_objects_with_schema/py.typed +0 -0
- {logging_objects_with_schema-1.0.0 → logging_objects_with_schema-1.0.2}/src/logging_objects_with_schema/schema_applier.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: logging-objects-with-schema
|
|
3
|
-
Version: 1.0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 1.0.2
|
|
4
|
+
Summary: Schema-aware Python logging with strict extra validation and schema-driven field projection.
|
|
5
5
|
Keywords: logging,schema,validation,structured-logging,structured-logs
|
|
6
6
|
Author: Dmitrii Safronov
|
|
7
7
|
Author-email: Dmitrii Safronov <dhameoelin+logging-objects-with-schema@gmail.com>
|
|
@@ -23,11 +23,12 @@ Description-Content-Type: text/markdown
|
|
|
23
23
|
|
|
24
24
|
# python-logging-objects-with-schema
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
`SchemaLogger` extends Python's standard `logging.Logger` with schema-driven
|
|
27
|
+
validation and projection of `extra` fields. It preserves the standard logging
|
|
28
|
+
API while invalid structured data is dropped and reported separately.
|
|
29
|
+
|
|
30
|
+
The schema is defined in `logging_objects_with_schema.json`, located by
|
|
31
|
+
searching upward from the current working directory to the filesystem root.
|
|
31
32
|
|
|
32
33
|
## Schema as a contract
|
|
33
34
|
|
|
@@ -55,8 +56,9 @@ systems) can rely on a consistent log structure.
|
|
|
55
56
|
where `int` is declared, and `int` is **not** accepted where `float` is
|
|
56
57
|
declared. There are no implicit type conversions.
|
|
57
58
|
- All validation problems are aggregated and logged as a single ERROR message
|
|
58
|
-
**after** the log record has been emitted
|
|
59
|
-
|
|
59
|
+
**after** the log record has been emitted. Data-validation failures do not
|
|
60
|
+
raise from the logging call; valid fields are emitted and violations are
|
|
61
|
+
reported separately.
|
|
60
62
|
- Application code must only send `extra` fields that are described in the
|
|
61
63
|
schema and match the declared Python types. Any deviation from the schema
|
|
62
64
|
is considered a contract violation.
|
|
@@ -71,7 +73,8 @@ pip install logging-objects-with-schema
|
|
|
71
73
|
|
|
72
74
|
### Quickstart (complete working example)
|
|
73
75
|
|
|
74
|
-
First, create a schema file `logging_objects_with_schema.json` in
|
|
76
|
+
First, create a schema file `logging_objects_with_schema.json` in the current
|
|
77
|
+
working directory (or any parent directory up to the filesystem root):
|
|
75
78
|
|
|
76
79
|
```json
|
|
77
80
|
{
|
|
@@ -149,7 +152,7 @@ logger.info(
|
|
|
149
152
|
```python
|
|
150
153
|
from logging_objects_with_schema import SchemaLogger
|
|
151
154
|
|
|
152
|
-
# SchemaLogger
|
|
155
|
+
# SchemaLogger uses the standard logging API; data-validation failures do not raise.
|
|
153
156
|
# If the schema has problems, the application will be terminated after
|
|
154
157
|
# logging schema problems to stderr.
|
|
155
158
|
logging.setLoggerClass(SchemaLogger)
|
|
@@ -164,9 +167,12 @@ logger.info("processing", extra={"user_id": "not-an-int"}) # Wrong type
|
|
|
164
167
|
|
|
165
168
|
## Schema location and format
|
|
166
169
|
|
|
167
|
-
The schema file `logging_objects_with_schema.json`
|
|
168
|
-
|
|
169
|
-
|
|
170
|
+
The schema file `logging_objects_with_schema.json` is located by searching from
|
|
171
|
+
the current working directory upward to the filesystem root. The first file
|
|
172
|
+
found wins; the location of the script or package does not matter. A file in a
|
|
173
|
+
parent directory is used without warning, so run the application with a fixed
|
|
174
|
+
working directory (for example, `WORKDIR` in a container or `WorkingDirectory`
|
|
175
|
+
in systemd).
|
|
170
176
|
|
|
171
177
|
**Important**: If there are any problems with the schema (missing file, broken
|
|
172
178
|
JSON, invalid structure, etc.), the application is terminated after logging
|
|
@@ -366,7 +372,7 @@ All keys are merged together - they are not replaced, only supplemented.
|
|
|
366
372
|
- Each subclass must implement the logic to pass `forbidden_keys` to its parent
|
|
367
373
|
if it wants to propagate keys from its own subclasses
|
|
368
374
|
- The `forbidden_keys` parameter is optional - if not provided, only builtin
|
|
369
|
-
keys are used,
|
|
375
|
+
keys are used, preserving the default forbidden-key behavior
|
|
370
376
|
- `None` and empty `set()` are semantically equivalent for `forbidden_keys` -
|
|
371
377
|
both mean "no additional forbidden keys" and produce the same result
|
|
372
378
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# python-logging-objects-with-schema
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
`SchemaLogger` extends Python's standard `logging.Logger` with schema-driven
|
|
4
|
+
validation and projection of `extra` fields. It preserves the standard logging
|
|
5
|
+
API while invalid structured data is dropped and reported separately.
|
|
6
|
+
|
|
7
|
+
The schema is defined in `logging_objects_with_schema.json`, located by
|
|
8
|
+
searching upward from the current working directory to the filesystem root.
|
|
8
9
|
|
|
9
10
|
## Schema as a contract
|
|
10
11
|
|
|
@@ -32,8 +33,9 @@ systems) can rely on a consistent log structure.
|
|
|
32
33
|
where `int` is declared, and `int` is **not** accepted where `float` is
|
|
33
34
|
declared. There are no implicit type conversions.
|
|
34
35
|
- All validation problems are aggregated and logged as a single ERROR message
|
|
35
|
-
**after** the log record has been emitted
|
|
36
|
-
|
|
36
|
+
**after** the log record has been emitted. Data-validation failures do not
|
|
37
|
+
raise from the logging call; valid fields are emitted and violations are
|
|
38
|
+
reported separately.
|
|
37
39
|
- Application code must only send `extra` fields that are described in the
|
|
38
40
|
schema and match the declared Python types. Any deviation from the schema
|
|
39
41
|
is considered a contract violation.
|
|
@@ -48,7 +50,8 @@ pip install logging-objects-with-schema
|
|
|
48
50
|
|
|
49
51
|
### Quickstart (complete working example)
|
|
50
52
|
|
|
51
|
-
First, create a schema file `logging_objects_with_schema.json` in
|
|
53
|
+
First, create a schema file `logging_objects_with_schema.json` in the current
|
|
54
|
+
working directory (or any parent directory up to the filesystem root):
|
|
52
55
|
|
|
53
56
|
```json
|
|
54
57
|
{
|
|
@@ -126,7 +129,7 @@ logger.info(
|
|
|
126
129
|
```python
|
|
127
130
|
from logging_objects_with_schema import SchemaLogger
|
|
128
131
|
|
|
129
|
-
# SchemaLogger
|
|
132
|
+
# SchemaLogger uses the standard logging API; data-validation failures do not raise.
|
|
130
133
|
# If the schema has problems, the application will be terminated after
|
|
131
134
|
# logging schema problems to stderr.
|
|
132
135
|
logging.setLoggerClass(SchemaLogger)
|
|
@@ -141,9 +144,12 @@ logger.info("processing", extra={"user_id": "not-an-int"}) # Wrong type
|
|
|
141
144
|
|
|
142
145
|
## Schema location and format
|
|
143
146
|
|
|
144
|
-
The schema file `logging_objects_with_schema.json`
|
|
145
|
-
|
|
146
|
-
|
|
147
|
+
The schema file `logging_objects_with_schema.json` is located by searching from
|
|
148
|
+
the current working directory upward to the filesystem root. The first file
|
|
149
|
+
found wins; the location of the script or package does not matter. A file in a
|
|
150
|
+
parent directory is used without warning, so run the application with a fixed
|
|
151
|
+
working directory (for example, `WORKDIR` in a container or `WorkingDirectory`
|
|
152
|
+
in systemd).
|
|
147
153
|
|
|
148
154
|
**Important**: If there are any problems with the schema (missing file, broken
|
|
149
155
|
JSON, invalid structure, etc.), the application is terminated after logging
|
|
@@ -343,7 +349,7 @@ All keys are merged together - they are not replaced, only supplemented.
|
|
|
343
349
|
- Each subclass must implement the logic to pass `forbidden_keys` to its parent
|
|
344
350
|
if it wants to propagate keys from its own subclasses
|
|
345
351
|
- The `forbidden_keys` parameter is optional - if not provided, only builtin
|
|
346
|
-
keys are used,
|
|
352
|
+
keys are used, preserving the default forbidden-key behavior
|
|
347
353
|
- `None` and empty `set()` are semantically equivalent for `forbidden_keys` -
|
|
348
354
|
both mean "no additional forbidden keys" and produce the same result
|
|
349
355
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build>=0.9.11,<0.14.0"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "logging-objects-with-schema"
|
|
7
|
+
version = "1.0.2"
|
|
8
|
+
description = "Schema-aware Python logging with strict extra validation and schema-driven field projection."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
dependencies = []
|
|
12
|
+
license = "Apache-2.0"
|
|
13
|
+
license-files = ["LICENSE"]
|
|
14
|
+
keywords = [
|
|
15
|
+
"logging",
|
|
16
|
+
"schema",
|
|
17
|
+
"validation",
|
|
18
|
+
"structured-logging",
|
|
19
|
+
"structured-logs",
|
|
20
|
+
]
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Programming Language :: Python :: 3.14",
|
|
27
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
28
|
+
"Intended Audience :: Developers",
|
|
29
|
+
"Topic :: System :: Logging",
|
|
30
|
+
"Typing :: Typed",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[[project.authors]]
|
|
34
|
+
name = "Dmitrii Safronov"
|
|
35
|
+
email = "dhameoelin+logging-objects-with-schema@gmail.com"
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Source = "https://github.com/disafronov/python-logging-objects-with-schema"
|
|
39
|
+
Issues = "https://github.com/disafronov/python-logging-objects-with-schema/issues"
|
|
40
|
+
|
|
41
|
+
[dependency-groups]
|
|
42
|
+
dev = [
|
|
43
|
+
"pytest>=7.0.0",
|
|
44
|
+
"pytest-cov>=4.0.0",
|
|
45
|
+
"black>=23.0.0",
|
|
46
|
+
"isort>=5.12.0",
|
|
47
|
+
"flake8>=6.0.0",
|
|
48
|
+
"flake8-pyproject>=1.2.3",
|
|
49
|
+
"mypy>=1.18.1",
|
|
50
|
+
"bandit>=1.8.6",
|
|
51
|
+
"pre-commit>=3.5.0",
|
|
52
|
+
"vulture>=2.0.0",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[tool.isort]
|
|
56
|
+
profile = "black"
|
|
57
|
+
|
|
58
|
+
[tool.black]
|
|
59
|
+
line-length = 88
|
|
60
|
+
include = '\.pyi?$'
|
|
61
|
+
|
|
62
|
+
[tool.flake8]
|
|
63
|
+
max-line-length = 88
|
|
64
|
+
extend-ignore = [
|
|
65
|
+
"E203",
|
|
66
|
+
"W503",
|
|
67
|
+
]
|
|
68
|
+
exclude = [".venv"]
|
|
69
|
+
|
|
70
|
+
[tool.mypy]
|
|
71
|
+
files = ["src"]
|
|
72
|
+
strict = true
|
|
73
|
+
warn_unused_configs = true
|
|
74
|
+
warn_unreachable = true
|
|
75
|
+
|
|
76
|
+
[tool.coverage.run]
|
|
77
|
+
source = ["src"]
|
|
78
|
+
branch = true
|
|
79
|
+
|
|
80
|
+
[tool.coverage.report]
|
|
81
|
+
fail_under = 100
|
|
82
|
+
exclude_lines = [
|
|
83
|
+
"raise AssertionError",
|
|
84
|
+
"raise NotImplementedError",
|
|
85
|
+
"if __name__ == .__main__.:",
|
|
86
|
+
'@(abc\.)?abstractmethod',
|
|
87
|
+
]
|
|
88
|
+
show_missing = true
|
|
89
|
+
skip_covered = false
|
|
90
|
+
|
|
91
|
+
[tool.coverage.html]
|
|
92
|
+
directory = "htmlcov"
|
|
93
|
+
|
|
94
|
+
[tool.vulture]
|
|
95
|
+
min_confidence = 80
|
|
96
|
+
ignore_names = ["cls"]
|
|
97
|
+
paths = ["src"]
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = [ "uv_build
|
|
2
|
+
requires = [ "uv_build>=0.9.11,<0.14.0" ]
|
|
3
3
|
build-backend = "uv_build"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "logging-objects-with-schema"
|
|
7
|
-
version = "1.0.
|
|
8
|
-
description = "
|
|
7
|
+
version = "1.0.2"
|
|
8
|
+
description = "Schema-aware Python logging with strict extra validation and schema-driven field projection."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
11
11
|
dependencies = [ ]
|
|
@@ -54,47 +54,21 @@ dev = [
|
|
|
54
54
|
|
|
55
55
|
[tool.isort]
|
|
56
56
|
profile = "black"
|
|
57
|
-
line_length = 88
|
|
58
|
-
multi_line_output = 3
|
|
59
57
|
|
|
60
58
|
[tool.black]
|
|
61
59
|
line-length = 88
|
|
62
60
|
include = "\\.pyi?$"
|
|
63
|
-
extend-exclude = """
|
|
64
|
-
/(
|
|
65
|
-
\\.eggs
|
|
66
|
-
| \\.git
|
|
67
|
-
| \\.hg
|
|
68
|
-
| \\.mypy_cache
|
|
69
|
-
| \\.tox
|
|
70
|
-
| \\.venv
|
|
71
|
-
| build
|
|
72
|
-
| dist
|
|
73
|
-
)/
|
|
74
|
-
"""
|
|
75
61
|
|
|
76
62
|
[tool.flake8]
|
|
77
63
|
max-line-length = 88
|
|
78
64
|
extend-ignore = [ "E203", "W503" ]
|
|
79
|
-
exclude = [ ".venv"
|
|
65
|
+
exclude = [ ".venv" ]
|
|
80
66
|
|
|
81
67
|
[tool.mypy]
|
|
82
|
-
|
|
83
|
-
|
|
68
|
+
files = [ "src" ]
|
|
69
|
+
strict = true
|
|
84
70
|
warn_unused_configs = true
|
|
85
|
-
disallow_untyped_defs = true
|
|
86
|
-
disallow_incomplete_defs = true
|
|
87
|
-
check_untyped_defs = true
|
|
88
|
-
disallow_untyped_decorators = true
|
|
89
|
-
no_implicit_optional = true
|
|
90
|
-
warn_redundant_casts = true
|
|
91
|
-
warn_unused_ignores = true
|
|
92
|
-
warn_no_return = true
|
|
93
71
|
warn_unreachable = true
|
|
94
|
-
strict_equality = true
|
|
95
|
-
|
|
96
|
-
[tool.bandit]
|
|
97
|
-
exclude_dirs = [ ".venv", "__pycache__", ".git", "htmlcov", "tests" ]
|
|
98
72
|
|
|
99
73
|
[tool.coverage.run]
|
|
100
74
|
source = [ "src" ]
|
|
@@ -103,11 +77,8 @@ branch = true
|
|
|
103
77
|
[tool.coverage.report]
|
|
104
78
|
fail_under = 100
|
|
105
79
|
exclude_lines = [
|
|
106
|
-
"def __repr__",
|
|
107
|
-
"if self\\.debug",
|
|
108
80
|
"raise AssertionError",
|
|
109
81
|
"raise NotImplementedError",
|
|
110
|
-
"if 0:",
|
|
111
82
|
"if __name__ == .__main__.:",
|
|
112
83
|
"@(abc\\.)?abstractmethod"
|
|
113
84
|
]
|
|
@@ -119,6 +90,5 @@ directory = "htmlcov"
|
|
|
119
90
|
|
|
120
91
|
[tool.vulture]
|
|
121
92
|
min_confidence = 80
|
|
122
|
-
exclude = [ "__pycache__" ]
|
|
123
93
|
ignore_names = [ "cls" ]
|
|
124
|
-
paths = [ "src"
|
|
94
|
+
paths = [ "src" ]
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""Schema loading and validation for logging_objects_with_schema.
|
|
2
2
|
|
|
3
3
|
The schema is expected to live in a JSON file named
|
|
4
|
-
``logging_objects_with_schema.json`` in the
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
``logging_objects_with_schema.json`` in the current working directory or one
|
|
5
|
+
of its parent directories, up to the filesystem root; the first file found is
|
|
6
|
+
used. The schema defines which extra fields are allowed to be emitted and which
|
|
7
|
+
Python types they must have.
|
|
7
8
|
"""
|
|
8
9
|
|
|
9
10
|
import functools
|
|
@@ -313,7 +314,7 @@ def _get_schema_path() -> Path:
|
|
|
313
314
|
|
|
314
315
|
|
|
315
316
|
def _load_raw_schema(schema_path: Path) -> tuple[dict[str, Any], Path]:
|
|
316
|
-
"""Load raw JSON schema from the
|
|
317
|
+
"""Load raw JSON schema from the resolved schema file.
|
|
317
318
|
|
|
318
319
|
This function attempts to read and parse the schema file. If any problems
|
|
319
320
|
occur (file not found, I/O errors, invalid JSON, wrong top-level type),
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
This class extends the standard ``logging.Logger`` to validate and filter
|
|
4
4
|
user-provided ``extra`` fields according to a compiled JSON schema. When
|
|
5
5
|
validation problems are detected, they are logged as ERROR messages *after*
|
|
6
|
-
the log record has been emitted
|
|
7
|
-
|
|
6
|
+
the main log record has been emitted. The standard logging call API is
|
|
7
|
+
preserved, but ``extra`` handling intentionally differs from ``logging.Logger``.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import json
|
|
@@ -45,12 +45,14 @@ class SchemaLogger(logging.Logger):
|
|
|
45
45
|
|
|
46
46
|
This class extends :class:`logging.Logger` to add validation for
|
|
47
47
|
``extra`` fields according to a compiled JSON schema. It is designed
|
|
48
|
-
to be
|
|
48
|
+
to be installed via :func:`logging.setLoggerClass`. Logging calls retain
|
|
49
|
+
the standard API, while ``extra`` is schema-validated and projected.
|
|
49
50
|
|
|
50
|
-
The schema is loaded from ``logging_objects_with_schema.json``
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
problems are logged to stderr, and
|
|
51
|
+
The schema is loaded from ``logging_objects_with_schema.json`` found by
|
|
52
|
+
searching upward from the current working directory to the filesystem root
|
|
53
|
+
during initialization. If the schema cannot be loaded or validated, the
|
|
54
|
+
logger instance is not created, schema problems are logged to stderr, and
|
|
55
|
+
the application is terminated.
|
|
54
56
|
|
|
55
57
|
Example:
|
|
56
58
|
>>> import logging
|
|
@@ -117,8 +119,8 @@ class SchemaLogger(logging.Logger):
|
|
|
117
119
|
This method validates and filters the ``extra`` parameter according
|
|
118
120
|
to the compiled schema before delegating to the parent class. If
|
|
119
121
|
validation problems are detected, they are logged as ERROR messages
|
|
120
|
-
after the main log record has been emitted
|
|
121
|
-
|
|
122
|
+
after the main log record has been emitted. Invalid runtime ``extra``
|
|
123
|
+
values are reported separately instead of raising validation errors.
|
|
122
124
|
|
|
123
125
|
Args:
|
|
124
126
|
level: Logging level.
|
|
@@ -185,7 +187,6 @@ class SchemaLogger(logging.Logger):
|
|
|
185
187
|
sinfo, # sinfo - stack info from caller
|
|
186
188
|
)
|
|
187
189
|
try:
|
|
188
|
-
self.
|
|
190
|
+
self.handle(error_record)
|
|
189
191
|
except Exception:
|
|
190
|
-
# If handler failed, log error to stderr (standard logging behavior)
|
|
191
192
|
sys.stderr.write(f"Error in logging handler: {error_record}\n")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|