structlog-config 0.6.0__tar.gz → 0.7.0__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,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: structlog-config
3
- Version: 0.6.0
3
+ Version: 0.7.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
@@ -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.
@@ -103,6 +103,64 @@ For example, if you wanted to [mimic `OPENAI_LOG` functionality](https://github.
103
103
  * `LOG_LEVEL_HTTPX=DEBUG`
104
104
  * `LOG_PATH_HTTPX=tmp/openai.log`
105
105
 
106
+ ## Custom Formatters
107
+
108
+ This package includes several custom formatters that automatically clean up log output:
109
+
110
+ ### Path Prettifier
111
+
112
+ Automatically formats `pathlib.Path` and `PosixPath` objects to show relative paths when possible, removing the wrapper class names:
113
+
114
+ ```python
115
+ from pathlib import Path
116
+ log.info("Processing file", file_path=Path.cwd() / "data" / "users.csv")
117
+ # Output: file_path=data/users.csv (instead of PosixPath('/home/user/data/users.csv'))
118
+ ```
119
+
120
+ ### Whenever Datetime Formatter
121
+
122
+ **Note:** Requires `pip install whenever` to be installed.
123
+
124
+ Formats [whenever](https://github.com/ariebovenberg/whenever) datetime objects without their class wrappers for cleaner output:
125
+
126
+ ```python
127
+ from whenever import ZonedDateTime
128
+
129
+ log.info("Event scheduled", event_time=ZonedDateTime(2025, 11, 2, 0, 0, 0, tz="UTC"))
130
+ # Output: event_time=2025-11-02T00:00:00+00:00[UTC]
131
+ # Instead of: event_time=ZonedDateTime("2025-11-02T00:00:00+00:00[UTC]")
132
+ ```
133
+
134
+ Supports all whenever datetime types: `ZonedDateTime`, `Instant`, `LocalDateTime`, `PlainDateTime`, etc.
135
+
136
+ ### ActiveModel Object Formatter
137
+
138
+ **Note:** Requires `pip install activemodel` and `pip install typeid-python` to be installed.
139
+
140
+ Automatically converts [ActiveModel](https://github.com/iloveitaly/activemodel) BaseModel instances to their ID representation and TypeID objects to strings:
141
+
142
+ ```python
143
+ from activemodel import BaseModel
144
+
145
+ user = User(id="user_123", name="Alice")
146
+ log.info("User action", user=user)
147
+ # Output: user_id=user_123 (instead of full object representation)
148
+ ```
149
+
150
+ ### FastAPI Context
151
+
152
+ **Note:** Requires `pip install starlette-context` to be installed.
153
+
154
+ Automatically includes all context data from [starlette-context](https://github.com/tomwojcik/starlette-context) in your logs, useful for request tracing:
155
+
156
+ ```python
157
+ # Context data (request_id, correlation_id, etc.) automatically included in all logs
158
+ log.info("Processing request")
159
+ # Output includes: request_id=abc-123 correlation_id=xyz-789 ...
160
+ ```
161
+
162
+ 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.
163
+
106
164
  ## FastAPI Access Logger
107
165
 
108
166
  **Note:** Requires `pip install structlog-config[fastapi]` for FastAPI dependencies.
@@ -176,6 +234,16 @@ FAILED tests/test_user.py::test_user_login
176
234
 
177
235
  For passing tests, no log output is shown, keeping your test output clean and focused.
178
236
 
237
+ ## Beautiful Traceback Support
238
+
239
+ 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:
240
+
241
+ ```bash
242
+ uv add beautiful-traceback --group dev
243
+ ```
244
+
245
+ No configuration needed - just install and `configure_logger()` will use it automatically.
246
+
179
247
  ## iPython
180
248
 
181
249
  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,6 +1,6 @@
1
1
  [project]
2
2
  name = "structlog-config"
3
- version = "0.6.0"
3
+ version = "0.7.0"
4
4
  description = "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
  readme = "README.md"
@@ -27,8 +27,10 @@ module-root = ""
27
27
 
28
28
  [dependency-groups]
29
29
  dev = [
30
+ "beautiful-traceback>=0.1.0",
30
31
  "fastapi>=0.115.12",
31
32
  "httpx>=0.28.1",
32
33
  "pytest>=8.3.3",
33
34
  "fastapi_ipware>=0.1.0",
35
+ "whenever>=0.9.3",
34
36
  ]
@@ -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
 
@@ -64,8 +65,8 @@ def log_processors_for_mode(json_logger: bool) -> list[structlog.types.Processor
64
65
  return [
65
66
  structlog.dev.ConsoleRenderer(
66
67
  colors=not NO_COLOR,
67
- exception_formatter=pretty_traceback_exception_formatter
68
- if packages.pretty_traceback
68
+ exception_formatter=beautiful_traceback_exception_formatter
69
+ if packages.beautiful_traceback
69
70
  else structlog.dev.default_exception_formatter,
70
71
  )
71
72
  ]
@@ -85,6 +86,7 @@ def get_default_processors(json_logger) -> list[structlog.types.Processor]:
85
86
  if packages.activemodel and packages.typeid
86
87
  else None,
87
88
  PathPrettifier(),
89
+ WheneverFormatter() if packages.whenever else None,
88
90
  structlog.processors.TimeStamper(fmt="iso", utc=True),
89
91
  # add `stack_info=True` to a log and get a `stack` attached to the log
90
92
  structlog.processors.StackInfoRenderer(),
@@ -64,17 +64,17 @@ def logger_name(logger: Any, method_name: Any, event_dict: EventDict) -> EventDi
64
64
  return event_dict
65
65
 
66
66
 
67
- def pretty_traceback_exception_formatter(sio: TextIO, exc_info: ExcInfo) -> None:
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 pretty-traceback, so I've added a custom processor to use it.
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 pretty_traceback.formatting import exc_to_traceback_str
77
+ from beautiful_traceback.formatting import exc_to_traceback_str
78
78
 
79
79
  _, exc_value, traceback = exc_info
80
80
  # TODO support local_stack_only env var support
@@ -150,6 +150,26 @@ class RenameField:
150
150
  return event_dict
151
151
 
152
152
 
153
+ class WheneverFormatter:
154
+ """A processor for formatting whenever datetime objects.
155
+
156
+ Changes all whenever datetime objects (ZonedDateTime, Instant, PlainDateTime, etc.)
157
+ from their repr() format (e.g., ZonedDateTime("2025-11-02 00:00:00+00:00[UTC]"))
158
+ to their string format (e.g., 2025-11-02 00:00:00+00:00[UTC]).
159
+
160
+ This provides cleaner log output without the class wrapper.
161
+ """
162
+
163
+ def __call__(self, _, __, event_dict):
164
+ for key, value in event_dict.items():
165
+ # Check if the value has the _pywhenever module attribute
166
+ # This is a reliable way to detect whenever types without importing them
167
+ if hasattr(value, "__module__") and value.__module__.startswith("whenever"):
168
+ event_dict[key] = str(value)
169
+
170
+ return event_dict
171
+
172
+
153
173
  def add_fastapi_context(
154
174
  logger: logging.Logger,
155
175
  method_name: str,
@@ -23,11 +23,16 @@ except ImportError:
23
23
  typeid = None
24
24
 
25
25
  try:
26
- import pretty_traceback
26
+ import beautiful_traceback
27
27
  except ImportError:
28
- pretty_traceback = None
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