structlog-config 0.6.0__py3-none-any.whl → 0.8.0__py3-none-any.whl
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.
- structlog_config/__init__.py +10 -4
- structlog_config/formatters.py +24 -3
- structlog_config/packages.py +7 -2
- {structlog_config-0.6.0.dist-info → structlog_config-0.8.0.dist-info}/METADATA +70 -2
- {structlog_config-0.6.0.dist-info → structlog_config-0.8.0.dist-info}/RECORD +6 -6
- {structlog_config-0.6.0.dist-info → structlog_config-0.8.0.dist-info}/WHEEL +1 -1
structlog_config/__init__.py
CHANGED
|
@@ -11,9 +11,10 @@ from structlog.typing import FilteringBoundLogger
|
|
|
11
11
|
|
|
12
12
|
from structlog_config.formatters import (
|
|
13
13
|
PathPrettifier,
|
|
14
|
+
WheneverFormatter,
|
|
14
15
|
add_fastapi_context,
|
|
16
|
+
beautiful_traceback_exception_formatter,
|
|
15
17
|
logger_name,
|
|
16
|
-
pretty_traceback_exception_formatter,
|
|
17
18
|
simplify_activemodel_objects,
|
|
18
19
|
)
|
|
19
20
|
|
|
@@ -61,12 +62,16 @@ def log_processors_for_mode(json_logger: bool) -> list[structlog.types.Processor
|
|
|
61
62
|
structlog.processors.JSONRenderer(serializer=orjson_dumps_sorted),
|
|
62
63
|
]
|
|
63
64
|
|
|
65
|
+
# Passing None skips the ConsoleRenderer default, so use the explicit dev default.
|
|
66
|
+
exception_formatter = structlog.dev.default_exception_formatter
|
|
67
|
+
|
|
68
|
+
if packages.beautiful_traceback:
|
|
69
|
+
exception_formatter = beautiful_traceback_exception_formatter
|
|
70
|
+
|
|
64
71
|
return [
|
|
65
72
|
structlog.dev.ConsoleRenderer(
|
|
66
73
|
colors=not NO_COLOR,
|
|
67
|
-
exception_formatter=
|
|
68
|
-
if packages.pretty_traceback
|
|
69
|
-
else structlog.dev.default_exception_formatter,
|
|
74
|
+
exception_formatter=exception_formatter,
|
|
70
75
|
)
|
|
71
76
|
]
|
|
72
77
|
|
|
@@ -85,6 +90,7 @@ def get_default_processors(json_logger) -> list[structlog.types.Processor]:
|
|
|
85
90
|
if packages.activemodel and packages.typeid
|
|
86
91
|
else None,
|
|
87
92
|
PathPrettifier(),
|
|
93
|
+
WheneverFormatter() if packages.whenever else None,
|
|
88
94
|
structlog.processors.TimeStamper(fmt="iso", utc=True),
|
|
89
95
|
# add `stack_info=True` to a log and get a `stack` attached to the log
|
|
90
96
|
structlog.processors.StackInfoRenderer(),
|
structlog_config/formatters.py
CHANGED
|
@@ -64,19 +64,20 @@ def logger_name(logger: Any, method_name: Any, event_dict: EventDict) -> EventDi
|
|
|
64
64
|
return event_dict
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
def
|
|
67
|
+
def beautiful_traceback_exception_formatter(sio: TextIO, exc_info: ExcInfo) -> None:
|
|
68
68
|
"""
|
|
69
69
|
By default, rich and then better-exceptions is used to render exceptions when a ConsoleRenderer is used.
|
|
70
70
|
|
|
71
|
-
I prefer
|
|
71
|
+
I prefer beautiful-traceback, so I've added a custom processor to use it.
|
|
72
72
|
|
|
73
73
|
https://github.com/hynek/structlog/blob/66e22d261bf493ad2084009ec97c51832fdbb0b9/src/structlog/dev.py#L412
|
|
74
74
|
"""
|
|
75
75
|
|
|
76
76
|
# only available in dev
|
|
77
|
-
from
|
|
77
|
+
from beautiful_traceback.formatting import exc_to_traceback_str
|
|
78
78
|
|
|
79
79
|
_, exc_value, traceback = exc_info
|
|
80
|
+
assert traceback is not None
|
|
80
81
|
# TODO support local_stack_only env var support
|
|
81
82
|
formatted_exception = exc_to_traceback_str(exc_value, traceback, color=not NO_COLOR)
|
|
82
83
|
sio.write("\n" + formatted_exception)
|
|
@@ -150,6 +151,26 @@ class RenameField:
|
|
|
150
151
|
return event_dict
|
|
151
152
|
|
|
152
153
|
|
|
154
|
+
class WheneverFormatter:
|
|
155
|
+
"""A processor for formatting whenever datetime objects.
|
|
156
|
+
|
|
157
|
+
Changes all whenever datetime objects (ZonedDateTime, Instant, PlainDateTime, etc.)
|
|
158
|
+
from their repr() format (e.g., ZonedDateTime("2025-11-02 00:00:00+00:00[UTC]"))
|
|
159
|
+
to their string format (e.g., 2025-11-02 00:00:00+00:00[UTC]).
|
|
160
|
+
|
|
161
|
+
This provides cleaner log output without the class wrapper.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
def __call__(self, _, __, event_dict):
|
|
165
|
+
for key, value in event_dict.items():
|
|
166
|
+
# Check if the value has the _pywhenever module attribute
|
|
167
|
+
# This is a reliable way to detect whenever types without importing them
|
|
168
|
+
if hasattr(value, "__module__") and value.__module__.startswith("whenever"):
|
|
169
|
+
event_dict[key] = str(value)
|
|
170
|
+
|
|
171
|
+
return event_dict
|
|
172
|
+
|
|
173
|
+
|
|
153
174
|
def add_fastapi_context(
|
|
154
175
|
logger: logging.Logger,
|
|
155
176
|
method_name: str,
|
structlog_config/packages.py
CHANGED
|
@@ -23,11 +23,16 @@ except ImportError:
|
|
|
23
23
|
typeid = None
|
|
24
24
|
|
|
25
25
|
try:
|
|
26
|
-
import
|
|
26
|
+
import beautiful_traceback
|
|
27
27
|
except ImportError:
|
|
28
|
-
|
|
28
|
+
beautiful_traceback = None
|
|
29
29
|
|
|
30
30
|
try:
|
|
31
31
|
import starlette_context
|
|
32
32
|
except ImportError:
|
|
33
33
|
starlette_context = None
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
import whenever
|
|
37
|
+
except ImportError:
|
|
38
|
+
whenever = None
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: structlog-config
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: A comprehensive structlog configuration with sensible defaults for development and production environments, featuring context management, exception formatting, and path prettification.
|
|
5
5
|
Keywords: logging,structlog,json-logging,structured-logging
|
|
6
6
|
Author: Michael Bianco
|
|
7
7
|
Author-email: Michael Bianco <mike@mikebian.co>
|
|
8
8
|
Requires-Dist: orjson>=3.10.15
|
|
9
9
|
Requires-Dist: python-decouple-typed>=3.11.0
|
|
10
|
-
Requires-Dist:
|
|
10
|
+
Requires-Dist: fastapi-ipware>=0.1.1
|
|
11
11
|
Requires-Dist: structlog>=25.2.0
|
|
12
12
|
Requires-Dist: fastapi-ipware>=0.1.0 ; extra == 'fastapi'
|
|
13
13
|
Requires-Python: >=3.11
|
|
@@ -120,6 +120,64 @@ For example, if you wanted to [mimic `OPENAI_LOG` functionality](https://github.
|
|
|
120
120
|
* `LOG_LEVEL_HTTPX=DEBUG`
|
|
121
121
|
* `LOG_PATH_HTTPX=tmp/openai.log`
|
|
122
122
|
|
|
123
|
+
## Custom Formatters
|
|
124
|
+
|
|
125
|
+
This package includes several custom formatters that automatically clean up log output:
|
|
126
|
+
|
|
127
|
+
### Path Prettifier
|
|
128
|
+
|
|
129
|
+
Automatically formats `pathlib.Path` and `PosixPath` objects to show relative paths when possible, removing the wrapper class names:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from pathlib import Path
|
|
133
|
+
log.info("Processing file", file_path=Path.cwd() / "data" / "users.csv")
|
|
134
|
+
# Output: file_path=data/users.csv (instead of PosixPath('/home/user/data/users.csv'))
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Whenever Datetime Formatter
|
|
138
|
+
|
|
139
|
+
**Note:** Requires `pip install whenever` to be installed.
|
|
140
|
+
|
|
141
|
+
Formats [whenever](https://github.com/ariebovenberg/whenever) datetime objects without their class wrappers for cleaner output:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from whenever import ZonedDateTime
|
|
145
|
+
|
|
146
|
+
log.info("Event scheduled", event_time=ZonedDateTime(2025, 11, 2, 0, 0, 0, tz="UTC"))
|
|
147
|
+
# Output: event_time=2025-11-02T00:00:00+00:00[UTC]
|
|
148
|
+
# Instead of: event_time=ZonedDateTime("2025-11-02T00:00:00+00:00[UTC]")
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Supports all whenever datetime types: `ZonedDateTime`, `Instant`, `LocalDateTime`, `PlainDateTime`, etc.
|
|
152
|
+
|
|
153
|
+
### ActiveModel Object Formatter
|
|
154
|
+
|
|
155
|
+
**Note:** Requires `pip install activemodel` and `pip install typeid-python` to be installed.
|
|
156
|
+
|
|
157
|
+
Automatically converts [ActiveModel](https://github.com/iloveitaly/activemodel) BaseModel instances to their ID representation and TypeID objects to strings:
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from activemodel import BaseModel
|
|
161
|
+
|
|
162
|
+
user = User(id="user_123", name="Alice")
|
|
163
|
+
log.info("User action", user=user)
|
|
164
|
+
# Output: user_id=user_123 (instead of full object representation)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### FastAPI Context
|
|
168
|
+
|
|
169
|
+
**Note:** Requires `pip install starlette-context` to be installed.
|
|
170
|
+
|
|
171
|
+
Automatically includes all context data from [starlette-context](https://github.com/tomwojcik/starlette-context) in your logs, useful for request tracing:
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
# Context data (request_id, correlation_id, etc.) automatically included in all logs
|
|
175
|
+
log.info("Processing request")
|
|
176
|
+
# Output includes: request_id=abc-123 correlation_id=xyz-789 ...
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
All formatters are optional and automatically enabled when their respective dependencies are installed. They work seamlessly in both development (console) and production (JSON) logging modes.
|
|
180
|
+
|
|
123
181
|
## FastAPI Access Logger
|
|
124
182
|
|
|
125
183
|
**Note:** Requires `pip install structlog-config[fastapi]` for FastAPI dependencies.
|
|
@@ -193,6 +251,16 @@ FAILED tests/test_user.py::test_user_login
|
|
|
193
251
|
|
|
194
252
|
For passing tests, no log output is shown, keeping your test output clean and focused.
|
|
195
253
|
|
|
254
|
+
## Beautiful Traceback Support
|
|
255
|
+
|
|
256
|
+
Optional support for [beautiful-traceback](https://github.com/iloveitaly/beautiful-traceback) provides enhanced exception formatting with improved readability, smart coloring, path aliasing (e.g., `<pwd>`, `<site>`), and better alignment. Automatically activates when installed:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
uv add beautiful-traceback --group dev
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
No configuration needed - just install and `configure_logger()` will use it automatically.
|
|
263
|
+
|
|
196
264
|
## iPython
|
|
197
265
|
|
|
198
266
|
Often it's helpful to update logging level within an iPython session. You can do this and make sure all loggers pick up on it.
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
structlog_config/__init__.py,sha256=
|
|
1
|
+
structlog_config/__init__.py,sha256=jTw47yiht0XmINIDUMicHZQYF_757ALXopxgOkAEhaQ,6939
|
|
2
2
|
structlog_config/constants.py,sha256=O1nPnB29yZdqqaI7aeTUrimA3LOtA5WpP6BGPLWJvj8,510
|
|
3
3
|
structlog_config/env_config.py,sha256=_EJO0rgAKndRPSh4wuBaH3bui9F3nIpn8FaEkjAjZso,1737
|
|
4
4
|
structlog_config/environments.py,sha256=JpZYVVDGxEf1EaKdPdn6Jo-4wJK6SqF0ueFl7e2TBvI,612
|
|
5
5
|
structlog_config/fastapi_access_logger.py,sha256=CYZsww0AIcdfrU5Wgr6POwdJxJ5vMB96ttsYqoE30BU,4363
|
|
6
|
-
structlog_config/formatters.py,sha256=
|
|
6
|
+
structlog_config/formatters.py,sha256=4B485qMTn9y5LqV7uWPJbgJrdIW5IiyRS3A0PEjcrS4,6748
|
|
7
7
|
structlog_config/levels.py,sha256=LqXG4l01mIpxS2qI7PF_Vp9K7IrO0D5qU_x-Uo3LNuM,2372
|
|
8
|
-
structlog_config/packages.py,sha256=
|
|
8
|
+
structlog_config/packages.py,sha256=xO4wHPIhAwGG6jv0kHdCr9NHpoIFx4VUeRzmztXp2is,591
|
|
9
9
|
structlog_config/pytest_plugin.py,sha256=XBtef1KpuGV_RmXoWf13b0EUy69iAKMAk4-e5ZZnAuM,6819
|
|
10
10
|
structlog_config/stdlib_logging.py,sha256=iHApYdsAs_ZC7Y8NpbKs23AHAJB1qLUCZPvFd-Nf_4I,7381
|
|
11
11
|
structlog_config/trace.py,sha256=esSzTulaVr63H3iOYpHlU-NXPSwpEGS7GEYbv6Iq-3A,2118
|
|
12
12
|
structlog_config/warnings.py,sha256=gKEcuHWqH0BaKitJtQkv-uJ0Z3uCH5nn6k8qpqjR-RM,998
|
|
13
|
-
structlog_config-0.
|
|
14
|
-
structlog_config-0.
|
|
15
|
-
structlog_config-0.
|
|
13
|
+
structlog_config-0.8.0.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
14
|
+
structlog_config-0.8.0.dist-info/METADATA,sha256=SmpaIUr3-ELE2u_dOAvrVBJkj6IX33rVe-lZgz9PiCU,10995
|
|
15
|
+
structlog_config-0.8.0.dist-info/RECORD,,
|