import contextvars import logging from urllib.parse import urlparse # Create a context variable for the request ID request_id_var = contextvars.ContextVar("request_id", default="N/A") source_ip_var = contextvars.ContextVar("source_ip", default="N/A") source_device_var = contextvars.ContextVar("source_device", default="N/A") payload_var = contextvars.ContextVar("payload", default="N/A") user_id_var = contextvars.ContextVar("user_id", default="Anonymous") request_path_var = contextvars.ContextVar("request_path", default="") request_method_var = contextvars.ContextVar("request_method", default="") class ContextualFilter(logging.Filter): """ This filter gets the request_id from the django request and adds it to each log record. This way we do not have to explicitly retrieve/pass around the request id for each log message. """ def filter(self, log_record): try: log_record.request_id = request_id_var.get() log_record.source_ip = source_ip_var.get() log_record.source_device = source_device_var.get() log_record.payload = payload_var.get() log_record.user_id = user_id_var.get() except Exception: log_record.request_id = None log_record.source_ip = None log_record.source_device = None log_record.payload = None log_record.user_id = None return True def request_context_processor(logger, method_name, event_dict): event_dict["request_id"] = request_id_var.get() event_dict["source_ip"] = source_ip_var.get() event_dict["source_device"] = source_device_var.get() event_dict["path"] = request_path_var.get() event_dict["user_id"] = user_id_var.get() # Populate http attribute with request_id and path details if "http" not in event_dict: event_dict["http"] = {} event_dict["http"]["method"] = request_method_var.get() event_dict["http"]["request_id"] = request_id_var.get() request_path = request_path_var.get() if request_path: parsed = urlparse(request_path) url_details = { "path": parsed.path, "queryString": parsed.query, } event_dict["http"]["url_details"] = url_details return event_dict def filter_empty_values_processor(logger, method_name, event_dict): """ Remove empty, None, zero, or default values from log events to reduce noise. """ def should_filter_value(value): if value is None: return True if value == "": return True if value == "N/A": return True return False def should_preserve_key(key): # Preserve structlog internal keys if key.startswith("_"): return True # Preserve important log metadata if key in ("event", "logger", "level", "timestamp"): return True return False def clean_dict(d): if not isinstance(d, dict): return d cleaned = {} for key, value in d.items(): # Always preserve internal keys if should_preserve_key(key): cleaned[key] = value elif isinstance(value, dict): cleaned_dict = clean_dict(value) if cleaned_dict: # Only add non-empty dicts cleaned[key] = cleaned_dict elif not should_filter_value(value): cleaned[key] = value return cleaned return clean_dict(event_dict)