Skip to content

Commit bc807f5

Browse files
committed
[docs] Expand coobook, cleanup references
Fixes: #21, #22
1 parent ee2c128 commit bc807f5

File tree

4 files changed

+82
-6
lines changed

4 files changed

+82
-6
lines changed

docs/cookbook.md

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ Recipies for common tasks.
44

55
## Include all fields
66

7-
This can be achieved by setting `reserved_attrs=[]` when creating the formatter.
7+
By default Python JSON Logger will not include fields [defined in the standard library](https://docs.python.org/3/library/logging.html#logrecord-attributes) unless they are included in the format. Manually including all these fields is tedious and Python version specific. Instead of adding them as explicit fields, we can add them implicitly be ensuring they are not in the `reserver_attrs` argument of the formatter.
8+
9+
```python
10+
all_fields_formatter = JsonFormatter(reserved_attrs=[])
11+
```
812

913
## Custom Styles
1014

@@ -166,3 +170,75 @@ format = %(message)s
166170
class = pythonjsonlogger.jsonlogger.JsonFormatter
167171
```
168172

173+
## Logging Expensive to Compute Data
174+
175+
By the nature of Python's logging library, the JSON formatters will only ever run in handlers which are enabled for the given log level. This saves the performance hit of constructing JSON that is never used - but what about the data we pass into the logger? There are two options available to us: using if statements to avoid the call altogether, or using lazy string evaluation libraries.
176+
177+
!!! note
178+
The below strategies will work for data passed in the `msg` and `extra` arguments.
179+
180+
To avoid the logging calls we use `logger.isEnabledFor` to ensure that we only start constructing our log messages if the logger is enabled:
181+
182+
```python
183+
import logging
184+
import time
185+
186+
from pythonjsonlogger.json import JsonFormatter
187+
188+
def expensive_to_compute():
189+
time.sleep(5)
190+
return "world"
191+
192+
## Setup
193+
## -------------------------------------
194+
logger = logging.getLogger()
195+
handler = logging.StreamHandler()
196+
formatter = JsonFormatter()
197+
handler.setFormatter(formatter)
198+
logger.addHandler(handler)
199+
logger.setLevel(logging.INFO)
200+
201+
## Log Using isEnabledFor
202+
## -------------------------------------
203+
start = time.time()
204+
if logger.isEnabledFor(logging.INFO):
205+
logger.info(
206+
{
207+
"data": "hello {}".format(expensive_to_compute())
208+
}
209+
)
210+
print(f"Logging INFO using isEnabledFor took: {int(time.time() - start)}s")
211+
212+
start = time.time()
213+
if logger.isEnabledFor(logging.DEBUG):
214+
logger.debug(
215+
{
216+
"data": "hello {}".format(expensive_to_compute())
217+
}
218+
)
219+
print(f"Logging DEBUG using isEnabledFor took: {int(time.time() - start)}s")
220+
```
221+
222+
For lazy string evaluation we can take advantage of the fact that the default JSON encoders included in this package will call `str` on unkown objects. We can use this to build our own lazy string evaluators, or we can use an existing external package. Pre-existing solutions include: [`lazy-string`](https://pypi.org/project/lazy-string/)'s `LazyString` or [`stringlike`](https://pypi.org/project/stringlike/)'s `CachedLazyString`.
223+
224+
```python
225+
## Log Using lazy-string
226+
## -------------------------------------
227+
from lazy_string import LazyString as L
228+
229+
start = time.time()
230+
logger.info(
231+
{
232+
"data": L("hello {}".format, L(expensive_to_compute))
233+
}
234+
)
235+
print(f"Logging INFO using LazyString took: {int(time.time() - start)}s")
236+
237+
start = time.time()
238+
logger.debug(
239+
{
240+
"data": L("hello {}".format, L(expensive_to_compute))
241+
}
242+
)
243+
print(f"Logging DEBUG using LazyString took: {int(time.time() - start)}s")
244+
```

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ classifiers = [
3939
]
4040

4141
[project.urls]
42-
Homepage = "https://nhairs.github.io/python-json-logger/latest/"
42+
Homepage = "https://nhairs.github.io/python-json-logger"
4343
GitHub = "https://github.com/nhairs/python-json-logger"
4444

4545
[project.optional-dependencies]

src/pythonjsonlogger/core.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@
6666
RESERVED_ATTRS.sort()
6767

6868

69-
STYLE_STRING_TEMPLATE_REGEX = re.compile(r"\$\{(.+?)\}", re.IGNORECASE)
70-
STYLE_STRING_FORMAT_REGEX = re.compile(r"\{(.+?)\}", re.IGNORECASE)
71-
STYLE_PERCENT_REGEX = re.compile(r"%\((.+?)\)", re.IGNORECASE)
69+
STYLE_STRING_TEMPLATE_REGEX = re.compile(r"\$\{(.+?)\}", re.IGNORECASE) # $ style
70+
STYLE_STRING_FORMAT_REGEX = re.compile(r"\{(.+?)\}", re.IGNORECASE) # { style
71+
STYLE_PERCENT_REGEX = re.compile(r"%\((.+?)\)", re.IGNORECASE) # % style
7272

7373
## Type Aliases
7474
## -----------------------------------------------------------------------------

src/pythonjsonlogger/orjson.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""JSON Formatter using [msgspec](https://github.com/ijl/orjson)"""
1+
"""JSON Formatter using [orjson](https://github.com/ijl/orjson)"""
22

33
### IMPORTS
44
### ============================================================================

0 commit comments

Comments
 (0)