Open ppi-agray opened 1 month ago
Hi @ppi-agray,
Just so you're aware it looks like python-json-logger is currently unmaintained, that said I am working on a maintained fork.
Whilst the below examples have been tested using my fork, I'm pretty sure that this still applies to the original library as well.
You're correct that add_fields
is called on every logging call - that's because this is the method that converts the LogRecord
into the dict
that will be converted to JSON and logged.
There's probably a few ways that you can achieve this (what is preferable depends on your use-case).
import logging
import uuid
# https://github.com/nhairs/python-json-logger v3.1.0.rc2
from pythonjsonlogger.json import JsonFormatter
## Setup
## -----------------------------------------------------------------------------
logger = logging.getLogger("test")
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
logger.addHandler(handler)
## Solution 1
## -----------------------------------------------------------------------------
formatter = JsonFormatter()
handler.setFormatter(formatter)
def main_1():
print("========== MAIN 1 ==========")
for i in range(3):
request_id = uuid.uuid4().hex
logger.info("loop start", extra={"request_id": request_id})
logger.info(f"loop {i}", extra={"request_id": request_id})
logger.info("loop end", extra={"request_id": request_id})
return
main_1()
## Solution 2
## -----------------------------------------------------------------------------
REQUEST_ID: str | None = None
def get_request_id() -> str:
return REQUEST_ID
def generate_request_id():
global REQUEST_ID
REQUEST_ID = uuid.uuid4().hex
class RequestIdFilter(logging.Filter):
# Ref: https://docs.python.org/3/howto/logging-cookbook.html#using-filters-to-impart-contextual-information
def filter(self, record):
record.record_id = get_request_id()
return True
request_id_filter = RequestIdFilter()
logger.addFilter(request_id_filter)
def main_2():
print("========== MAIN 2 ==========")
for i in range(3):
generate_request_id()
logger.info("loop start")
logger.info(f"loop {i}")
logger.info("loop end")
return
main_2()
logger.removeFilter(request_id_filter)
## Solution 3
## -----------------------------------------------------------------------------
# Reuse REQUEST_ID stuff from solution 2
class MyFormatter(JsonFormatter):
def process_log_record(self, log_record):
log_record["request_id"] = get_request_id()
return log_record
handler.setFormatter(MyFormatter())
def main_3():
print("========== MAIN 3 ==========")
for i in range(3):
generate_request_id()
logger.info("loop start")
logger.info(f"loop {i}")
logger.info("loop end")
return
main_3()
I have a request to have a unique ID in the logs "per request". I can reproduce the problem that I'm trying to solve by this loop:
I'm hoping to have something like:
But instead I'm getting a unique ID PER message:
My full code is like this:
I was hoping the function
add_fields
would be called just once, when I'm setting the formatter, but it looks like it's called every time I print a log message.Is there a way to do this with
python-json-logger
?thanks!!!