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.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: logging-objects-with-schema
3
- Version: 1.0.0
4
- Summary: Proxy logging wrapper that validates extra fields against a JSON schema.
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
- This library provides a logger subclass built on top of the standard `logging`
27
- module that strictly controls additional `extra` fields using a JSON schema.
28
- `SchemaLogger` is a drop-in replacement for `logging.Logger` that validates
29
- `extra` fields against a JSON schema file (`logging_objects_with_schema.json`)
30
- in your application root directory.
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, ensuring 100% compatibility with
59
- standard logger behavior (no exceptions are raised).
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 your application root:
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 is a drop-in replacement - no exception handling needed.
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` must be located in your
168
- application root directory. The library searches upward from the current working
169
- directory until it finds the file or reaches the filesystem root.
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, maintaining 100% backward compatibility
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
- This library provides a logger subclass built on top of the standard `logging`
4
- module that strictly controls additional `extra` fields using a JSON schema.
5
- `SchemaLogger` is a drop-in replacement for `logging.Logger` that validates
6
- `extra` fields against a JSON schema file (`logging_objects_with_schema.json`)
7
- in your application root directory.
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, ensuring 100% compatibility with
36
- standard logger behavior (no exceptions are raised).
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 your application root:
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 is a drop-in replacement - no exception handling needed.
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` must be located in your
145
- application root directory. The library searches upward from the current working
146
- directory until it finds the file or reaches the filesystem root.
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, maintaining 100% backward compatibility
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 >= 0.9.11, <0.10.0" ]
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.0"
8
- description = "Proxy logging wrapper that validates extra fields against a JSON schema."
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", "__pycache__", ".git", "htmlcov" ]
65
+ exclude = [ ".venv" ]
80
66
 
81
67
  [tool.mypy]
82
- exclude = "tests/"
83
- warn_return_any = true
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", "tests" ]
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 application root
5
- directory. The schema defines which extra fields are allowed to be
6
- emitted and which Python types they must have.
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 application root.
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, ensuring 100% compatibility with standard
7
- logger behavior.
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 used as a drop-in replacement via :func:`logging.setLoggerClass`.
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`` in the
51
- application root directory during initialization. If the schema cannot
52
- be loaded or validated, the logger instance is not created, schema
53
- problems are logged to stderr, and the application is terminated.
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, ensuring compatibility
121
- with standard logger behavior (no exceptions are raised).
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.callHandlers(error_record)
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")