instruktai-python-logger 0.1.1__tar.gz → 0.1.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.
- {instruktai_python_logger-0.1.1 → instruktai_python_logger-0.1.2}/PKG-INFO +5 -4
- {instruktai_python_logger-0.1.1 → instruktai_python_logger-0.1.2}/README.md +4 -3
- {instruktai_python_logger-0.1.1 → instruktai_python_logger-0.1.2}/instrukt_ai_logging/logging.py +66 -42
- {instruktai_python_logger-0.1.1 → instruktai_python_logger-0.1.2}/instruktai_python_logger.egg-info/PKG-INFO +5 -4
- {instruktai_python_logger-0.1.1 → instruktai_python_logger-0.1.2}/pyproject.toml +1 -1
- {instruktai_python_logger-0.1.1 → instruktai_python_logger-0.1.2}/tests/test_configure_logging.py +2 -3
- {instruktai_python_logger-0.1.1 → instruktai_python_logger-0.1.2}/instrukt_ai_logging/__init__.py +0 -0
- {instruktai_python_logger-0.1.1 → instruktai_python_logger-0.1.2}/instrukt_ai_logging/cli.py +0 -0
- {instruktai_python_logger-0.1.1 → instruktai_python_logger-0.1.2}/instruktai_python_logger.egg-info/SOURCES.txt +0 -0
- {instruktai_python_logger-0.1.1 → instruktai_python_logger-0.1.2}/instruktai_python_logger.egg-info/dependency_links.txt +0 -0
- {instruktai_python_logger-0.1.1 → instruktai_python_logger-0.1.2}/instruktai_python_logger.egg-info/entry_points.txt +0 -0
- {instruktai_python_logger-0.1.1 → instruktai_python_logger-0.1.2}/instruktai_python_logger.egg-info/requires.txt +0 -0
- {instruktai_python_logger-0.1.1 → instruktai_python_logger-0.1.2}/instruktai_python_logger.egg-info/top_level.txt +0 -0
- {instruktai_python_logger-0.1.1 → instruktai_python_logger-0.1.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: instruktai-python-logger
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Centralized logging utilities for InstruktAI Python services.
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -40,11 +40,12 @@ pip install git+ssh://git@github.com/InstruktAI/python-logger.git
|
|
|
40
40
|
Example:
|
|
41
41
|
|
|
42
42
|
```py
|
|
43
|
-
|
|
43
|
+
import logging
|
|
44
|
+
from instrukt_ai_logging import configure_logging
|
|
44
45
|
|
|
45
46
|
configure_logging(name="teleclaude", app_logger_prefix="teleclaude")
|
|
46
|
-
logger =
|
|
47
|
-
logger.info("job_started", job_id="abc123")
|
|
47
|
+
logger = logging.getLogger("teleclaude.core")
|
|
48
|
+
logger.info("job_started", job_id="abc123", user_id=123)
|
|
48
49
|
```
|
|
49
50
|
|
|
50
51
|
## Environment variables (contract)
|
|
@@ -30,11 +30,12 @@ pip install git+ssh://git@github.com/InstruktAI/python-logger.git
|
|
|
30
30
|
Example:
|
|
31
31
|
|
|
32
32
|
```py
|
|
33
|
-
|
|
33
|
+
import logging
|
|
34
|
+
from instrukt_ai_logging import configure_logging
|
|
34
35
|
|
|
35
36
|
configure_logging(name="teleclaude", app_logger_prefix="teleclaude")
|
|
36
|
-
logger =
|
|
37
|
-
logger.info("job_started", job_id="abc123")
|
|
37
|
+
logger = logging.getLogger("teleclaude.core")
|
|
38
|
+
logger.info("job_started", job_id="abc123", user_id=123)
|
|
38
39
|
```
|
|
39
40
|
|
|
40
41
|
## Environment variables (contract)
|
{instruktai_python_logger-0.1.1 → instruktai_python_logger-0.1.2}/instrukt_ai_logging/logging.py
RENAMED
|
@@ -4,7 +4,6 @@ import logging
|
|
|
4
4
|
import os
|
|
5
5
|
import re
|
|
6
6
|
import sys
|
|
7
|
-
from collections.abc import Mapping
|
|
8
7
|
from dataclasses import dataclass
|
|
9
8
|
from datetime import datetime, timedelta, timezone
|
|
10
9
|
from logging.handlers import WatchedFileHandler
|
|
@@ -167,60 +166,79 @@ class LogfmtFormatter(UtcMillisFormatter):
|
|
|
167
166
|
return " ".join(parts)
|
|
168
167
|
|
|
169
168
|
|
|
170
|
-
|
|
171
|
-
"""
|
|
172
|
-
safe: dict[str, object] = {}
|
|
173
|
-
for key, value in kv.items():
|
|
174
|
-
if not isinstance(key, str):
|
|
175
|
-
continue
|
|
176
|
-
if key == "msg":
|
|
177
|
-
continue
|
|
178
|
-
if not _SAFE_KEY.fullmatch(key):
|
|
179
|
-
continue
|
|
180
|
-
safe[key] = value
|
|
181
|
-
|
|
182
|
-
logger.log(level, msg, extra={"kv": safe})
|
|
183
|
-
|
|
169
|
+
class InstruktLogger(logging.Logger):
|
|
170
|
+
"""Logger that accepts arbitrary `**kv` fields.
|
|
184
171
|
|
|
185
|
-
|
|
186
|
-
|
|
172
|
+
This lets callers use normal logger methods with named key/value pairs:
|
|
173
|
+
logging.getLogger("...").info("event_name", job_id=..., user_id=...)
|
|
187
174
|
|
|
188
|
-
|
|
189
|
-
logger.info("event_name", job_id=..., user_id=...)
|
|
190
|
-
|
|
191
|
-
This keeps call sites human-friendly while still producing strict logfmt pairs.
|
|
175
|
+
All `**kv` values are serialized to text by the formatter.
|
|
192
176
|
"""
|
|
193
177
|
|
|
194
|
-
def
|
|
195
|
-
|
|
178
|
+
def _log_with_kv(self, level: int, msg: object, args: tuple[object, ...], **kwargs: Any) -> None:
|
|
179
|
+
exc_info = kwargs.pop("exc_info", None)
|
|
180
|
+
stack_info = kwargs.pop("stack_info", False)
|
|
181
|
+
stacklevel = kwargs.pop("stacklevel", 1)
|
|
182
|
+
extra = kwargs.pop("extra", None)
|
|
183
|
+
|
|
184
|
+
# Remaining kwargs are treated as `kv`.
|
|
185
|
+
kv: dict[str, object] = {k: v for k, v in kwargs.items() if _SAFE_KEY.fullmatch(k)}
|
|
186
|
+
|
|
187
|
+
merged_extra: dict[str, object] = {}
|
|
188
|
+
if isinstance(extra, dict):
|
|
189
|
+
merged_extra.update(extra)
|
|
190
|
+
|
|
191
|
+
existing_kv = merged_extra.get("kv")
|
|
192
|
+
if isinstance(existing_kv, dict):
|
|
193
|
+
merged_extra["kv"] = {**existing_kv, **kv}
|
|
194
|
+
else:
|
|
195
|
+
merged_extra["kv"] = kv
|
|
196
|
+
|
|
197
|
+
super()._log(
|
|
198
|
+
level,
|
|
199
|
+
msg,
|
|
200
|
+
args,
|
|
201
|
+
exc_info=exc_info,
|
|
202
|
+
extra=merged_extra,
|
|
203
|
+
stack_info=stack_info,
|
|
204
|
+
stacklevel=stacklevel,
|
|
205
|
+
)
|
|
196
206
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
207
|
+
def debug(self, msg: object, *args: object, **kwargs: Any) -> None: # type: ignore[override]
|
|
208
|
+
if self.isEnabledFor(logging.DEBUG):
|
|
209
|
+
self._log_with_kv(logging.DEBUG, msg, args, **kwargs)
|
|
200
210
|
|
|
201
|
-
def
|
|
202
|
-
|
|
211
|
+
def info(self, msg: object, *args: object, **kwargs: Any) -> None: # type: ignore[override]
|
|
212
|
+
if self.isEnabledFor(logging.INFO):
|
|
213
|
+
self._log_with_kv(logging.INFO, msg, args, **kwargs)
|
|
203
214
|
|
|
204
|
-
def
|
|
205
|
-
|
|
215
|
+
def warning(self, msg: object, *args: object, **kwargs: Any) -> None: # type: ignore[override]
|
|
216
|
+
if self.isEnabledFor(logging.WARNING):
|
|
217
|
+
self._log_with_kv(logging.WARNING, msg, args, **kwargs)
|
|
206
218
|
|
|
207
|
-
def
|
|
208
|
-
|
|
219
|
+
def error(self, msg: object, *args: object, **kwargs: Any) -> None: # type: ignore[override]
|
|
220
|
+
if self.isEnabledFor(logging.ERROR):
|
|
221
|
+
self._log_with_kv(logging.ERROR, msg, args, **kwargs)
|
|
209
222
|
|
|
210
|
-
def
|
|
211
|
-
|
|
223
|
+
def critical(self, msg: object, *args: object, **kwargs: Any) -> None: # type: ignore[override]
|
|
224
|
+
if self.isEnabledFor(logging.CRITICAL):
|
|
225
|
+
self._log_with_kv(logging.CRITICAL, msg, args, **kwargs)
|
|
212
226
|
|
|
213
|
-
def exception(self, msg:
|
|
214
|
-
|
|
215
|
-
self.
|
|
227
|
+
def exception(self, msg: object, *args: object, **kwargs: Any) -> None: # type: ignore[override]
|
|
228
|
+
kwargs.setdefault("exc_info", True)
|
|
229
|
+
if self.isEnabledFor(logging.ERROR):
|
|
230
|
+
self._log_with_kv(logging.ERROR, msg, args, **kwargs)
|
|
216
231
|
|
|
217
|
-
def log(self, level: int, msg:
|
|
218
|
-
|
|
232
|
+
def log(self, level: int, msg: object, *args: object, **kwargs: Any) -> None: # type: ignore[override]
|
|
233
|
+
if not isinstance(level, int):
|
|
234
|
+
raise TypeError("level must be an int")
|
|
235
|
+
if self.isEnabledFor(level):
|
|
236
|
+
self._log_with_kv(level, msg, args, **kwargs)
|
|
219
237
|
|
|
220
238
|
|
|
221
|
-
def get_logger(name: str) ->
|
|
222
|
-
"""
|
|
223
|
-
return
|
|
239
|
+
def get_logger(name: str) -> logging.Logger:
|
|
240
|
+
"""Compatibility helper (returns a normal logger, but with `**kv` support after configuration)."""
|
|
241
|
+
return logging.getLogger(name)
|
|
224
242
|
|
|
225
243
|
|
|
226
244
|
class _ThirdPartySelectorFilter(logging.Filter):
|
|
@@ -312,6 +330,12 @@ def configure_logging(
|
|
|
312
330
|
app_name=app_name,
|
|
313
331
|
)
|
|
314
332
|
|
|
333
|
+
# Ensure all loggers accept arbitrary `**kv` (no wrapper at call sites).
|
|
334
|
+
logging.setLoggerClass(InstruktLogger)
|
|
335
|
+
for obj in logging.root.manager.loggerDict.values():
|
|
336
|
+
if isinstance(obj, logging.Logger) and not isinstance(obj, InstruktLogger):
|
|
337
|
+
obj.__class__ = InstruktLogger
|
|
338
|
+
|
|
315
339
|
our_level_name = (os.getenv(contract.env_log_level) or "INFO").upper()
|
|
316
340
|
third_party_level_name = (os.getenv(contract.env_third_party_level) or "WARNING").upper()
|
|
317
341
|
spotlight = _parse_csv(os.getenv(contract.env_third_party_loggers, ""))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: instruktai-python-logger
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Centralized logging utilities for InstruktAI Python services.
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -40,11 +40,12 @@ pip install git+ssh://git@github.com/InstruktAI/python-logger.git
|
|
|
40
40
|
Example:
|
|
41
41
|
|
|
42
42
|
```py
|
|
43
|
-
|
|
43
|
+
import logging
|
|
44
|
+
from instrukt_ai_logging import configure_logging
|
|
44
45
|
|
|
45
46
|
configure_logging(name="teleclaude", app_logger_prefix="teleclaude")
|
|
46
|
-
logger =
|
|
47
|
-
logger.info("job_started", job_id="abc123")
|
|
47
|
+
logger = logging.getLogger("teleclaude.core")
|
|
48
|
+
logger.info("job_started", job_id="abc123", user_id=123)
|
|
48
49
|
```
|
|
49
50
|
|
|
50
51
|
## Environment variables (contract)
|
{instruktai_python_logger-0.1.1 → instruktai_python_logger-0.1.2}/tests/test_configure_logging.py
RENAMED
|
@@ -4,7 +4,7 @@ from tempfile import TemporaryDirectory
|
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
-
from instrukt_ai_logging import configure_logging
|
|
7
|
+
from instrukt_ai_logging import configure_logging
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
@pytest.fixture()
|
|
@@ -94,8 +94,7 @@ def test_named_kv_logger_emits_pairs(isolated_logging, monkeypatch):
|
|
|
94
94
|
name="teleclaude",
|
|
95
95
|
)
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
logger.info("hello", session="abc123", n=1)
|
|
97
|
+
logging.getLogger("teleclaude.core").info("hello", session="abc123", n=1)
|
|
99
98
|
|
|
100
99
|
content = _read_text(log_path)
|
|
101
100
|
assert 'msg="hello"' in content
|
{instruktai_python_logger-0.1.1 → instruktai_python_logger-0.1.2}/instrukt_ai_logging/__init__.py
RENAMED
|
File without changes
|
{instruktai_python_logger-0.1.1 → instruktai_python_logger-0.1.2}/instrukt_ai_logging/cli.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|